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 typeC
-
C
overrides thespeak()
method ofB
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 typeB
(superclass) and assigns it to object of typeC
(subclass) -
C
"is-a"B
soC
can be assigned to variable of typeB
-
Even though
b
is typeB
, it still is referring to an object that is typeC
Code Review
What is the output of A.f() in the following?
- Although
b
is declared as typeB
, the actual type that the variable is referring to isC
.
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 typeC
and is reassigned to typeB
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:
- Pre-condition - What inputs does it expect?
- Post-condition - What outputs does it guarantee?
- 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:
- Put a breakpoint on line 14 and run the tests in Debug Mode.
- Different features of Debug Mode:
- The variables section
- The debug console
- The 'watch' section
- The call stack Debug control
- 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?
COMP2511 Tutorial 3
By rebeccahsu
COMP2511 Tutorial 3
- 677