COMP2511 Week 4

Agenda

  • Admin Stuff
  • Law of Demeter
  • Liskov Substitution Principle
  • Streams and Lambda
  • Design by Contract

Admin Stuff

  • Assignment 1 is due this Friday
    • Make sure to push your code to the master branch
    • Put a submission tag on it
    • There is an assignment viva (discussion with me) after it's due in week 5/7.
  • Get feedback on assignment UML, I'm happy to go through it in our lab or through email.
  • Assignment 2 finding a pair
    • If I didn't ask you for your buddy last week or you don't know what I'm talking about, talk to me after class. 

Law of Demeter

"Principle of least knowledge"

Law of Demeter

What is it?

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 this actually mean?

A method in an object should only call methods of:

  • The object itself
  • The object passed in as a parameter to the method
  • Objects instantiated within the method

 

Only talk to your immediate friends

E.g. Don't do this!

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

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).

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;
    }
}
/**
 * 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;
    }
}
/**
 * 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;
}

What line is law of demeter broken on?

Code Review

What other properties of this design are not desirable?

 

Code Review

What other properties of this design are not desirable?

  • The design is needlessly tightly coupled as TrainingSystem is dependent on both Trainer and Seminar
  • TrainingSystem suffers from low cohesion as any change to the system requires a change to this class.
  • The Seminar class has no control over the number of attendees. It relies on TrainingSystem to ensure there are never more than 10. This makes Seminar hard to re-use.
public class TrainingSystem {
    public List<Trainer> trainers;
    
    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;
    }

    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;
    }
}
/**
 * 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 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;
    }
}
/**
 * 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;
}

Liskov Substitution Principle (LSP)

Liskov Substitution Principle(LSP)

What is it?

Liskov Substitution Principle(LSP)

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.

Liskov Substitution Principle(LSP)

Where does OnlineSeminar violate LSP?

/**
 * 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;
    }
}
/**
 * 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;
}

Liskov Substitution Principle(LSP)

Where does OnlineSeminar violate LSP?

  • OnlineSeminar doesn't require a list of attendees
/**
 * 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;
    }
}
/**
 * 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;
}

Streams and Lambda

Streams and Lambda

Streams abstract away the details of data structures and allows you to access all the values in the data structure through a common interface 

Common uses of streams are:

  • forEach
  • filter
  • map
  • reduce

Streams and Lambda

Inside src/stream/App.java, rewrite the following code using the .forEach() method and a lambda.

 

List<String> strings = new ArrayList<String>(Arrays.asList(new String[] {"1", "2", "3", "4", "5"}));
for (String string : strings) {
   System.out.println(string);
}

In the above example, discuss two different ways to write lambda expressions.

 

Streams and Lambda

  1. Rewrite the following code to use a stream and the map function.
  2. Modify your answer to use a scope operator instead of a normal lambda.

 

List<String> strings2 = new ArrayList<String>(Arrays.asList(new String[] {"1", "2", "3", "4", "5"}));
List<Integer> ints = new ArrayList<Integer>();
for (String string : strings2) {
    ints.add(Integer.parseInt(string));
}

Design by Contract

What is it?

Design by Contract

What is it?


At the design time, responsibilities are clearly assigned to different software elements, clearly documented and enforced during the development using unit testing and/or language support.

  • Clear distinction of responsibilities helps prevent redundant checks, resulting in simpler code and easier maintenance.
  • Crashes if the required conditions are not satisfied! May not be suitable for high availability applications.

Design by Contract

Every software element should define a specification (or a contract) that govern its transaction with the rest of the software components.

A contract should address the following 3 conditions:

  1. Pre-condition - what does the contract expect?
  2. Post-condition - what does that contract guarantee?
  3. Invariant - What does the contract maintain?

Design by Contract

Design by Contract

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

    public static Double subtract(Double a, Double b) {
        return a - b;
    }

    public static Double multiply(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 cos(Double angle) {
        return Math.cos(angle);
    }

    public static Double tan(Double angle) {
        return Math.tan(angle);
    }
}
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
     * @postconditions a - b
     */
    public static Double subtract(Double a, Double b) {
        return a - b;
    }

    /**
     * @preconditions a, b != null
     * @postconditions a * b
     */
    public static Double multiply(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
     * @postconditions cos(angle)
     */
    public static Double cos(Double angle) {
        return Math.cos(angle);
    }

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