COMP2511

25T2 Week 4

Friday 9AM - 12PM (F09A)

 

Slides by Christian Tolentino (z5420628)

Make sure to fill in the assignment 1 pair preference form (due Friday)!

Today

  • Law of Demeter
  • Liskov Substitution Principle
  • Composite pattern
  • Factory pattern

Law of Demeter

"Principle of least knowledge"

Law of Demeter

What is it?

Law of Demeter (aka principle of least knowledge) is a design guideline that says that an object should assume as little as possible knowledge about the structures or properties of other objects.


It aims to achieve loose coupling in code.

Law of Demeter

What does it actually mean?

A method in an object should only invoke methods of:

  • The object itself
  • The object passed in as a parameter to the method
  • Objects instantiated within the method
  • Any component objects
  • And not those of objects returned by a method

 

E.g., don't do this

o.get(name).get(thing).remove(node)

This is not a hard-and-fast rule. Sometimes it is unavoidable.

Code Review

Law of Demeter

Code Review

In the  unsw.training  package there is some skeleton code for a training system.

  • Every employee must attend a whole day training seminar run by a qualified trainer
  • Each trainer is running multiple seminars with no more than 10 attendees per seminar

In the TrainingSystem class there is a method to book a seminar for an employee given the dates on which they are available. This method violates the principle of least knowledge (Law of Demeter).

Code Review

In the unsw.training package there is some skeleton code for a training system.

  • Every employee must attend a whole day training seminar run by a qualified trainer
  • Each trainer is running multiple seminars with no more than 10 attendees per seminar

In the TrainingSystem class there is a method to book a seminar for an employee given the dates on which they are available. This method violates the principle of least knowledge (Law of Demeter).

/**
 * An online seminar is a video that can be viewed at any time by employees. A
 * record is kept of which employees have watched the seminar.
 */
public class OnlineSeminar extends Seminar {
    private String videoURL;
    private List<String> watched;
}
/**
 * An in person all day seminar with a maximum of 10 attendees.
 */
public class Seminar {
    private LocalDate start;
    private List<String> attendees;

    public LocalDate getStart() {
        return start;
    }

    public List<String> getAttendees() {
        return attendees;
    }
}
public class TrainingSystem {
    private List<Trainer> trainers;

    public LocalDate bookTraining(String employee, List<LocalDate> availability) {
        for (Trainer trainer : trainers) {
            for (Seminar seminar : trainer.getSeminars()) {
                for (LocalDate available : availability) {
                    if (seminar.getStart().equals(available) &&
                            seminar.getAttendees().size() < 10) {
                        seminar.getAttendees().add(employee);
                        return available;
                    }
                }
            }
        }
        return null;
    }
}
/**
 * A trainer that runs in person seminars.
 */
public class Trainer {
    private String name;
    private String room;
    private List<Seminar> seminars;

    public List<Seminar> getSeminars() {
        return seminars;
    }
}

How and why does it violate this principle?

What other properties of this design are not desirable?

/**
 * An online seminar is a video that can be viewed at any time by employees. A
 * record is kept of which employees have watched the seminar.
 */
public class OnlineSeminar extends Seminar {
    private String videoURL;
    private List<String> watched;
}
/**
 * An in person all day seminar with a maximum of 10 attendees.
 */
public class Seminar {
    private LocalDate start;
    private List<String> attendees;
    public LocalDate getStart() {
        return start;
    }

    /**
     * Try to book this seminar if it occurs on one of the available days and
     * isn't already full
     * @param employee
     * @param availability
     * @return The date of the seminar if booking was successful, null otherwise
     */
    public LocalDate book(String employee, List<LocalDate> availability) {
        for (LocalDate available : availability) {
            if (start.equals(available) &&
                    attendees.size() < 10) {
                attendees.add(employee);
                return available;
            }
        }
        return null;
    }
}
public class TrainingSystem {
    public List<Trainer> trainers;
    /**
     * Try to booking training for an employee, given their availability.
     *
     * @param employee
     * @param availability
     * @return The date of their seminar if booking was successful, null there
     * are no empty slots in seminars on the day they are available.
     */
    public LocalDate bookTraining(String employee, List<LocalDate> availability) {
        for (Trainer trainer : trainers) {
            LocalDate booked = trainer.book(employee, availability);
            if (booked != null)
                return booked;
        }
        return null;
    }
}
/**
 * A trainer that runs in person seminars.
 */
public class Trainer {
    private String name;
    private String room;
    private List<Seminar> seminars;

    public List<Seminar> getSeminars() {
        return seminars;
    }

    /**
     * Try to book one of this trainer's seminars.
     * @param employee
     * @param availability
     * @return The date of the seminar if booking was successful, null if the
     * trainer has no free slots in seminars on the available days.
     */
    public LocalDate book(String employee, List<LocalDate> availability) {
        for (Seminar seminar : seminars) {
            LocalDate booked = seminar.book(employee, availability);
            if (booked != null)
                return booked;
        }
        return null;
    }
}
  1. TrainingSystem no longer has knowledge of Seminar
  2. Each class has their own responsibility (good cohesion)

Liskov Substitution Principle

Liskov Substitution Principle

What is it?

Liskov Substitution Principle (LSP) states that objects of a superclass should be replaceable with objects of its subclasses without breaking the application.

*inheritance arrows are the other way around

Liskov Substitution Principle

Solve the problem without inheritance

  • Delegation - delegate the functionality to another class
  • Composition - reuse behaviour using one or more classes with composition
     

Design principle: Favour composition over inheritance

If you favour composition over inheritance, your software will be more flexible, easier to maintain, extend.

Liskov Substitution Principle

/**
 * An online seminar is a video that can be viewed at any time by employees. A
 * record is kept of which employees have watched the seminar.
 */
public class OnlineSeminar extends Seminar {
    private String videoURL;
    private List<String> watched;
}
/**
 * An in person all day seminar with a maximum of 10 attendees.
 */
public class Seminar {
    private LocalDate start;
    private List<String> attendees;

    public LocalDate getStart() {
        return start;
    }

    public List<String> getAttendees() {
        return attendees;
    }
}

Where does OnlineSeminar violate LSP?

OnlineSeminar doesn't require a list of attendees

Composite Pattern

Composite Pattern

What type of design pattern is composite?

Structural

Structural design pattern are patterns that ease the design by identifying a simple way to realize relationships among entities.

They explain how to assemble objects and classes into large structures, while keeping structures flexible and efficient

Composite pattern is useful for aggregating different objects/data. The aim is to be able to manipulate a single instance of an object just as you would manipulate a group of them.

Tree like structure of objects

Composite Pattern

  • No discrimination between a single (leaf) or a group (composite). Keeps code clean

Composite Pattern

Code Demo

Calculator.java - Composite pattern

Code Demo

Inside src/calculator, use the Composite Pattern to write a simple calculator that evaluates an expression. Your calculator should be able to:

  • Add two expressions
  • Subtract two expressions
  • Multiply two expressions
  • Divide two expressions

Code Demo

{1 + [2 * (4 + 3)]}

Expression 1

Expression 2

Expression 3

Factory Pattern

Factory Pattern

What type of design pattern?

Creational

Creational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code.

 

Factory method provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.

Reduces coupling and shotgun surgery, as all classes are created using the same method.

Factory Pattern

  1. The Product declares the interface, which is common to all objects that can be produced by the Creator and its subclasses.
  2. Concrete products are different implementations of the product interface
  3. The Creator class declares the factory method and returns new product objects
  4. Concrete Creators override the base factory method so it returns a new type of product

Code Demo

Thrones.java - Factory Pattern

Code Demo

Inside src/thrones, there is some code to model a simple chess-like game. In this game different types of characters move around on a grid fighting each other. When one character moves into the square occupied by another they attack that character and inflict damage based on random chance. There are four types of characters:

  • A king can move one square in any direction (including diagonally), and always causes 8 points of damage when attacking.
  • A knight can move like a knight in chess (in an L shape), and has a 1 in 2 chance of inflicting 10 points of damage when attacking.
  • A queen can move to any square in the same column, row or diagonal as she is currently on, and has a 1 in 3 chance of inflicting 12 points of damage or a 2 out of 3 chance of inflicting 6 points of damage.
  • A troll can only move up, down, left or right, and has a 1 in 6 chance of inflicting 20 points of damage.

Code Demo

We want to refactor the code so that when the characters are created, they are put in a random location in a grid of length 5.

  1. How does the Factory Pattern (AKA Factory Method) allow us to abstract construction of objects, and how will it improve our design with this new requirement?
    • Abstract the construction of the character objects. We don't deal with the constructor, instead call a general factory method that handles the number.
  2. Use the Factory Pattern to create a series of object factories for each of the character types, and change the main method of Game.java to use these factories.

COMP2511 Week 4 25T2

By Christian Tolentino

COMP2511 Week 4 25T2

  • 74