COMP2511 Tute03

Agenda

  • Admin Stuff
  • Code Review
  • Design by Contract (DbC)
  • Liskov Substitution Principle (LSP)
  • Domain Modeling (UML)
  • Code Demo

Admin Stuff

  • Assignment-i is out! Please start early :)
    • Due Week 5 Friday, 3pm
  • Go to help sessions if you need help
    • They get busier towards the due date
  • Don't auto-generate UML diagrams. You will get no marks. 

Code Review

Code Review

public class A {
    public static void f() {
        C c = new C();
        c.speak();
        B b = c;
        b.speak();
        b = new B();
        b.speak();
        c.speak();
    }
}

public class B {
    public void speak() {
        System.out.println("moo");
    }
}

public class C extends B {
    public void speak() {
        System.out.println("quack");
    }
}

Can you override a static method?

Code Review

public class A {
    public static void f() {
        C c = new C();
        c.speak();
        B b = c;
        b.speak();
        b = new B();
        b.speak();
        c.speak();
    }
}

public class B {
    public void speak() {
        System.out.println("moo");
    }
}

public class C extends B {
    public void speak() {
        System.out.println("quack");
    }
}

Can you override a static method?

No, because a static method is attached to the class and not a particular instance

Code Review

public class A {
    public static void f() {
        C c = new C();
        c.speak();
        B b = c;
        b.speak();
        b = new B();
        b.speak();
        c.speak();
    }
}

public class B {
    public void speak() {
        System.out.println("moo");
    }
}

public class C extends B {
	@Override
    public void speak() {
        System.out.println("quack");
    }
}

What is the output of A.f() in the following?

Code Review

What is the output of A.f() in the following?

public class A {
    public static void f() {
        C c = new C();
        c.speak();
        B b = c;
        b.speak();
        b = new B();
        b.speak();
        c.speak();
    }
}

public class B {
    public void speak() {
        System.out.println("moo");
    }
}

public class C extends B {
	@Override
    public void speak() {
        System.out.println("quack");
    }
}
  • Create object c of type C 
  • C overrides the speak() method of B

 

    c.speak() -> "quack"

Code Review

public class A {
    public static void f() {
        C c = new C();
        c.speak();
        B b = c;
        b.speak();
        b = new B();
        b.speak();
        c.speak();
    }
}

public class B {
    public void speak() {
        System.out.println("moo");
    }
}

public class C extends B {
    public void speak() {
        System.out.println("quack");
    }
}

What is the output of A.f() in the following?

  • Creates a variable b of type B (superclass) and assigns it to object of type C (subclass) 
  • C "is-a" B so C can be assigned to variable of type B

 

  • Even though b is type B, it still is referring to an object that is type C

Code Review

What is the output of A.f() in the following?

  • Although b is declared as type B, the actual type that the variable is referring to is C.

 

    c.speak() -> "quack"

public class A {
    public static void f() {
        C c = new C();
        c.speak();
        B b = c;
        b.speak();
        b = new B();
        b.speak();
        c.speak();
    }
}

public class B {
    public void speak() {
        System.out.println("moo");
    }
}

public class C extends B {
    public void speak() {
        System.out.println("quack");
    }
}

Code Review

What is the output of A.f() in the following?

  • Creates a new object of type B
  • b no longer refers to type C and is reassigned to type B

 

    b.speak() -> "moo"

public class A {
    public static void f() {
        C c = new C();
        c.speak();
        B b = c;
        b.speak();
        b = new B();
        b.speak();
        c.speak();
    }
}

public class B {
    public void speak() {
        System.out.println("moo");
    }
}

public class C extends B {
    public void speak() {
        System.out.println("quack");
    }
}

Code Review

What is the output of A.f() in the following?

 

    c.speak() -> "quack"

public class A {
    public static void f() {
        C c = new C();
        c.speak();
        B b = c;
        b.speak();
        b = new B();
        b.speak();
        c.speak();
    }
}

public class B {
    public void speak() {
        System.out.println("moo");
    }
}

public class C extends B {
    public void speak() {
        System.out.println("quack");
    }
}

Code Review

public class A {
    public static void f() {
        C c = new C();
        c.speak();
        B b = c;
        b.speak();
        b = new B();
        b.speak();
        c.speak();
    }
}

public class B {
    public void speak() {
        System.out.println("moo");
    }
}

public class C extends B {
    public void speak() {
        System.out.println("quack");
    }
}

What is the output of A.f() in the following? 

quack quack moo quack

Code Review

public class A {
    public static void f() {
        B b1 = new B();
        B b2 = new B();
        b1.incX();
        b2.incY();
        System.out.println(b1.getX() + " " + b1.getY());
        System.out.println(b2.getX() + " " + b2.getY());
    }
}

public class B {
    private int x = 0;
    private static int y = 0;

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void incX() {
        x++;
    }

    public void incY() {
        y++;
    }
}

What is the output of A.f() in the following?

Code Review

public class A {
    public static void f() {
        B b1 = new B();
        B b2 = new B();
        b1.incX();
        b2.incY();
        System.out.println(b1.getX() + " " + b1.getY());
        System.out.println(b2.getX() + " " + b2.getY());
    }
}

public class B {
    private int x;
    private static int y;

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void incX() {
        x++;
    }

    public void incY() {
        y++;
    }
}

What is the output of A.f() in the following?

b1: x=1, y=0

b2: x=0, y=0

Code Review

public class A {
    public static void f() {
        B b1 = new B();
        B b2 = new B();
        b1.incX();
        b2.incY();
        System.out.println(b1.getX() + " " + b1.getY());
        System.out.println(b2.getX() + " " + b2.getY());
    }
}

public class B {
    private int x;
    private static int y;

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void incX() {
        x++;
    }

    public void incY() {
        y++;
    }
}

What is the output of A.f() in the following?

b1: x=1, y=1

b2: x=0, y=1

Design by Contract

Design by Contract

Software development approach which provides an interface for others to use with clear preconditions, postconditions and invariants which, when adhered to, guarantees correct and expected behaviour

What is it?

A contract should address the following 3 conditions:

  1. Pre-condition - What inputs does it expect?
  2. Post-condition - What outputs does it guarantee?
  3. Invariant - What is always maintained before and after a function call? 

Design by Contract

Defensive Programming

Alternative development approach is defensive programming.

This is where code actively checks and guards against potential unexpected or invalid input (e.g. throwing exceptions) 

Design by Contract

public class Calculator {
    public static Double add(Double a, Double b) {
        return a + b;
    }

    public static Double divide(Double a, Double b) {
        return a / b;
    }

    public static Double sin(Double angle) {
        return Math.sin(angle);
    }

    public static Double tan(Double angle) {
        return Math.tan(angle);
    }
}

Specify a contract for each function

i.e. preconditions & postconditions

Design by Contract

public class Calculator {
    /**
     * @preconditions a, b != null
     * @postconditions a + b
     */
    public static Double add(Double a, Double b) {
        return a + b;
    }

    /**
     * @preconditions a, b != null, b != 0
     * @postconditions a / b
     */
    public static Double divide(Double a, Double b) {
        return a / b;
    }

    /**
     * @preconditions angle != null
     * @postconditions sin(angle)
     */
    public static Double sin(Double angle) {
        return Math.sin(angle);
    }

    /**
     * @preconditions angle != null, angle != Math.PI / 2
     * @postconditions tan(angle)
     */
    public static Double tan(Double angle) {
        return Math.tan(angle);
    }
}

Design by Contract in Assignment 1

  • We specified interface functions in the Controller. So long as you matched the interface, you implement the functions however you like. 

 

  • We told you specifically that you didn't have to account for invalid cases (e.g. all IDs are unique across entities). 
    • This is example of precondition which you didn't have to account for (undefined behaviour)

Liskov Substitution Principle (LSP)

Liskov Substitution Principle
 

What is it?

Design principle that states subclasses must be substitutable for their superclasses. To be substitutable, the subclass must behave like the superclass.

In this example, Car and Bicycle violate LSP. 

But Plane does not violate LSP. 

LSP with Preconditions and Postconditions

Subclasses must NOT:

  • Strengthen preconditions
  • Weaken postconditions

 

In other words, subclass must:

  • Accept the superset of the values the parent method accepts 
  • Output a subset of the parent's possible outputs

LSP Example

class Bird {
	/**
     * @precondition height > 5
     * @postcondition bird is now flying at that height
     */
    public void fly(int height) {
        System.out.println("Bird is now flying at " + 
        	height + " metres");
    }
}
class Penguin extends Bird {
	/**
     * @precondition height == 0 (penguins can't fly)
     * @postcondition nothing changes to height of bird
     */
    @Override
    public void fly(int height) {
        System.out.println("Penguin is still not flying...");
    }
}

Are our postconditions and preconditions being strengthened or weakened?

Preconditions strengthened

  • Stronger set of conditions accepted by child is not good
  • Certain heights that is accepted by parent (e.g 6 m) is no longer accepted by subclass. 

Postconditions weakened

  • Weaker set of guarantees promised by child is not good
  • Guarantees made by parent (i.e. bird is gonna fly at this height) is not promised by subclass (i.e. no flying, height is the same)

Superclass 

Precondition: 0 < theta < 180

 

Subclass

Precondition: 0 < theta < 90

Example of LSP with Satellites

Here, we have a subclass that goes against the previous rule, in strengthening the precondition in the subclass.

Superclass 

Precondition: 0 < theta < 180

 

Subclass

Precondition: 0 < theta < 90

Example of LSP with Satellites

If we give an input of 150, the input can be accepted by the superclass but NOT the subclass

So, the subclass is not acting like the superclass.

THIS VIOLATES LSP

Code Demo

People

Domain Modeling using UML

UML Classes

This class has 2 attributes and 4 operations. 

getBalance() returns a float

parameter of withDraw() 

is of type float

+ Public attribute

- Private attribute

# Protected attribute

UML Relationships

 

Inheritance: "is-a" relationship

Realisation: "implements" relationship

Aggregation: "part of" relationship

  • if A aggregates B and A is destroyed, B can still exist
  • e.g. Course is aggregate of students

 

Composition: "has-a" relationship

  • is a special form of aggregation
  • if A composes B and A is destroyed, B can no longer exist
  • e.g. House composes of rooms

 

UML Aggregation

"has-a" relationship

  • The diamond is on the side of the container class
  • If you remove the container class, the child class should still be able to exist

UML Composition

"has-a" relationship

  • Like aggregation, the diamond is on the side of the container class
  • If you remove the container class, the child class CANNOT exist

UML Classes

Angle brackets

Italicised

UML Cardinality

 

Types of cardinality

  • one to one
  • one to many 
  • many to many

Domain Modeling

Create an OO domain model for a system with the following requirements.

A Car has one or more engines and a producer. The producer is a manufacturing company who has a brand name. Engines are produced by a producer and have a speed. There are only two types of engines within UNSW's cars:

  • Thermal Engines, which have a default max speed of 114, although they can be produced with a different max speed, and the max speed can change to any value between 100 and 250.
  • Electrical Engines, which have a default max speed of 180. This is the speed at which they are produced, and the max speed can change to any value that is divisible by 6.
     

Cars are able to drive to a particular location x, y.
 

Since UNSW is a world-leader in technology innovation, they want you to be able to model the behaviour of Time Travelling for any vehicle, and to model a time travelling car. A vehicle that travels in time stays in the same location but travels to a LocalDateTime.

 

LucidCharts

Generally, a good diagram maker that is free*

* if you sign up using .edu account

Code Demo

Wonderous.java

Code Demo

The Wondrous Sequence is generated by the simple rule:

  • If the current term is even, the next term is half the current term.
  • If the current term is odd, the next term is three times the current term, plus 1.

 

For example, the sequence generated by starting with 3 is:

         3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
If the starting term is 1, then an empty list is returned.

Part 1 - Debugging

Explore the IDE tools built into VSCode by:

  1. Put a breakpoint on line 14 and run the tests in Debug Mode.
  2. Different features of Debug Mode:
    1. The variables section
    2. The debug console
    3. The 'watch' section
    4. The call stack Debug control
    5. Use the debug tools and the given algorithm to determine why the test is failing, and fix the bug.

Part 2 - Writing JUnit Tests

There is a further bug in the function not caught by the given unit test. Find the other bug, and write a corresponding unit test inside WondrousTest.

Part 3 - Exceptional Conditions

  • What are exceptions?

Part 3 - Exceptional Conditions

  • What are exceptions?
    • An exception is an event, which occurs during the execution of a problem, that disrupts the normal flow of the program's instructions
  • What are the 2 types of exceptions?
Checked Unchecked
Occur at compile time and used for recoverable conditions. Method that uses checked must either be handled (try-catch) or declared in method signature (throws keyword). Errors in the program's logic which occur at runtime mostly due to programming mistakes. 
Any class that inherits from Exception except RuntimeException is a checked exception. E.g., IOException, SQLException Any class that inherits from RuntimeException is unchecked E.g., ArrayIndexOutOfBoundsExceptions, ArithmeticException

Part 3 - Exceptional Conditions

  • What are the two types of exceptions?

Checked Exception Example

import java.io.*;

public class TryCatchExample {
    public static void main(String[] args) {
        try {
            FileReader file = new FileReader("file.txt"); // May throw IOException
            file.read();
            file.close();
        } catch (IOException e) { // Handle the exception
            System.out.println("An error occurred while reading the file: " + e.getMessage());
        }
    }
}
import java.sql.*;

public class ThrowsExample {
    public static void connectToDatabase() throws SQLException { // Declaring exception
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "user", "password");
        Statement stmt = conn.createStatement();
        stmt.executeQuery("SELECT * FROM users"); // May throw SQLException
    }

    public static void main(String[] args) {
        try {
            connectToDatabase(); // Must handle or propagate exception
        } catch (SQLException e) {
            System.out.println("Database connection failed: " + e.getMessage());
        }
    }
}

try-catch

throws declaration

Unchecked Exception Example

public class UncheckedExceptionExample {
    public static void main(String[] args) {
        int num = 10 / 0; // Causes ArithmeticException (unchecked)
    }
}

Part 3 - Exceptional Conditions

Modify the method such that if a start is less than 1, an IllegalArgumentException is thrown. Write a corresponding test for this inside WondrousTest.

 

In many cases when we throw an exception we need to update the method signature and existing tests but here we don't - why is this?

 

Answer

COMP2511 Tutorial 3

By rebeccahsu

COMP2511 Tutorial 3

  • 677