COMP2511

25T2 Week 5

Friday 1pm-2pm (F13A)

 

Start 1:05pm

 

By: Sam Zheng (z5418112)

Original Slides by: Alvin Cherk

This week

  • Streams & Lambdas
  • Design Patterns:
    • Strategy pattern
    • Observer pattern

Please make sure your lab marks are up to date and what you expect.

Assignment 1 due today 3pm!

Lab Due Dates

  • Flex week does not count as a lab week
  • Lab 4 - due Week 5 Monday, latest day to get marked is Week 8 Monday
  • Lab 5 - due Week 7 Monday, latest day to get marked is Week 9 Monday

VSCode Live Preview

  • Finding it annoying to bring up the test coverage report in the browser?
  • Install Live Preview by Microsoft in VSCode Extensions
  • Locate HTML file of test report
  • Click 'Show Preview' button in top right
  • See test report right in VSCode; it also updates live when you re-run the coverage tests

Code Demo

Streams

Code Demo

Convert the following to use streams

package stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

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

        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));
        }
        System.out.println(ints);
    }

}
package stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class App {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<String>(Arrays.asList(new String[] { "1", "2", "3", "4", "5" }));
        // Same thing
        strings.stream().forEach(x -> System.out.println(x));
        // Use if there is more than one line of code needed in lambda
        strings.stream().forEach(x -> {
            System.out.println(x);
        });

        List<String> strings2 = new ArrayList<String>(Arrays.asList(new String[] { "1", "2", "3", "4", "5" }));
        List<Integer> parsedStrings = strings2.stream().map(x -> Integer.parseInt(x)).collect(Collectors.toList());
        strings2.stream().map(x -> Integer.parseInt(x)).forEach(x -> System.out.println(x));
    }
}

Strategy Pattern

Strategy Pattern

Problem: Only some of the children of a parent class implement an abstract method the same way.

Question: How would you implement this without duplicating code?

Note: Behaviours are only shared downwards in inheritance

Strategy Pattern

Problem: Only some of the children of a parent class implement an abstract method the same way.

We could introduce a new class in-between the parent and child class.

Strategy Pattern

We could introduce a new class in-between the parent and child class.

However, this becomes problematic when we the Ducks have other methods that share the same implementation.

Strategy Pattern

A solution is to move this behaviour into another class and compose this class inside Duck

This is one of the main benefits of the strategy pattern, sharing behaviour across an inheritance tree

Strategy Pattern

What type of design pattern is strategy?

Behavioural Pattern

Behavioural patterns are patterns concerned with algorithms and the assignment of responsibility between object

  • Uses composition instead of inheritance
  • Allows for dependency injection (Selects and adapts an algorithm at run time). Change the behaviour at runtime.
  • Encapsulates interchangeable behaviours and uses delegation to decide which one to use
  • Useful when you want to share behaviour across an inheritance tree

Strategy Pattern

Code Demo

Strategy Pattern

Restaurant payment system with the following requirements:

  • The restaurant has a menu, stored in a JSON file. Each meal on the menu has a name and price
  • The system displays all of the standard meal names and their prices to the user so they can make their order
  • The user can enter their order as a series of meals, and the system returns their cost
  • The prices on meals often vary in different circumstances. The restaurant has four different price settings:
  • Standard - normal rates
    • Holiday - 15% surcharge on all items for all customers
    • Happy Hour - where registered members get a 40% discount, while standard customers get 30%

The prices displayed on the menu are the ones for standard customers in all settings

public class Restaurant {
    ...
    public double cost(List<Meal> order, String payee) {
        switch (chargingStrategy) {
        case "standard":
            return order.stream().mapToDouble(meal -> meal.getCost()).sum();
        case "holiday":
            return order.stream().mapToDouble(meal -> meal.getCost() * 1.15).sum();
        case "happyHour":
            if (members.contains(payee)) {
                return order.stream().mapToDouble(meal -> meal.getCost() * 0.6).sum();
            } else {
                return order.stream().mapToDouble(meal -> meal.getCost() * 0.7).sum();
            }
        default:
            return 0;
        }
    }
    ...
}
  1. How does the code violate the open/closed principle?
  2. How does this make the code brittle?

Not closed for modification, open for extension. If more cases need to be added, the switch statement has to be changed.

New requirements may cause the code to break or may be difficult to implement

public class Restaurant {
    ...
    public void displayMenu() {
        double modifier = 0;
        switch (chargingStrategy) {
        case "standard":
            modifier = 1;
            break;
        case "holiday":
            modifier = 1.15;
            break;
        case "happyHour":
            modifier = 0.7;
            break;
        }

        for (Meal meal : menu) {
            System.out.println(meal.getName() + " - " + meal.getCost() * modifier);
        }
    }
    ...
}

Similar idea here, if new cases need to be added, the class' method itself needs to be changed. Cannot be extended

Code Demo - Strategy

To fix these issues, we can introduce a strategy pattern and move all the individual case logic into their own classes

public interface ChargingStrategy {
    /**
     * The cost of a meal.
     */
    public double cost(List<Meal> order, boolean payeeIsMember);

    /**
     * Modifying factor of charges for standard customers.
     */
    public double standardChargeModifier();

}

Observer Pattern

Observer Pattern

What type of design pattern is strategy?

Behavioural Pattern

An object (subject) maintains a list of dependents called observers. The subject notifies the observers automatically of any state changes.

  • Used to implement event handling systems ("event driven" programming).
  • Able to dynamically add and remove observers
  • One-to-many dependency such that when the subject changes state, all of its dependents (observers) are notified and updated automatically
  • Loosing coupling of objects that interact with each other.

Observer Pattern

Observer

Subject

These are primarily based on the Java built-ins. They are deprecated however, avoid using them!

Code Demo

Observer Pattern

Code Demo

In src/youtube, create a model for the following requirements of a Youtube-like video creating and watching service using the Observer Pattern:

  • A Producer has a name, a series of subscribers and videos
  • When a producer posts a new video, all of the subscribers are notified that a new video was posted
  • A User has a name, and can subscribe to any Producer
  • A video has a name, length and producer

COMP2511 Week 5 25T2

By Sam Zheng

COMP2511 Week 5 25T2

  • 148