WJ20228 OOP10 DesignPatterns
WJ20228 OOP10 DesignPatterns
Object-Oriented Programming
Design Patterns
Object-Oriented Programming 1
Design Patterns Goals
▪ Discuss why design patterns are important and what advantages they provide.
Object-Oriented Programming 2
Design Patterns Design Patterns are Everywhere
▪ In 1995, a book was published by the “Gang of Four” called Design Patterns.
• It applied the concept of patterns (discussed next) to software design and described 23 of them.
- The authors did not invent these patterns.
- Instead, they included patterns they found in at least 3 “real” software systems.
▪ Since that time lots of Design Patterns books have been published.
• And more patterns have been cataloged.
▪ Unfortunately, many people feel like they should become experts in object-oriented
analysis and design before they learn about patterns.
• The book takes a different stance: learning about design patterns will help you become an
expert in object-oriented analysis and design.
Object-Oriented Programming 3
Design Patterns Christopher Alexander (I)
▪ Design patterns in software design traces its intellectual roots to work performed in the
1970s by an architect named Christopher Alexander.
• His 1979 book called “The Timeless Way of Building” that asks the question “Is quality
objective?”.
- In particular, “What makes us know when an architectural design is good? Is there an objective basis for
such a judgement?”.
• His answer was “yes” that it was possible to objectively define “high quality” or “beautiful”
buildings.
▪ He studied the problem of identifying what makes a good architectural design by
observing all sorts of built structures.
• Buildings, towns, streets, homes, community centers, etc.
▪ When he found an example of a high quality design, he would compare that object to
other objects of high quality and look for commonalties.
• Especially if both objects were used to solve the same type of problem.
Object-Oriented Programming 4
Design Patterns Christopher Alexander (II)
▪ By studying high quality structures that solve similar problems, he could discover
similarities between the designs and these similarities where what he called patterns.
• “Each pattern describes a problem which occurs over and over again in our environment and
then describes the core of the solution to that problem, in such a way that you can use this
solution a million times over, without ever doing it the same way twice”.
• The pattern provides an approach that can be used to achieve a high quality solution to its
problem.
Object-Oriented Programming 5
Design Patterns Four Elements of a Pattern
Object-Oriented Programming 6
Design Patterns Design Patterns and Software
Object-Oriented Programming 7
Design Patterns Key Features of a Pattern
▪ “Descriptions of communicating objects and classes that are customized to solve a general design
problem in a particular context.”
[Gamma, Helm, Johnson, Vlissides 1995]
▪ Name
▪ Intent: The purpose of the pattern.
▪ Problem: What problem does it solve?
▪ Solution: The approach to take to solve the problem.
▪ Participants: The entities involved in the pattern.
▪ Consequences: The effect the pattern has on your system.
▪ Structure: Class Diagram.
▪ Implementation: Example ways to implement the pattern.
▪ Sample code
▪ Related patterns
Object-Oriented Programming 8
Design Patterns Why Study Design Patterns?
▪ Patterns let us
• reuse solutions that have worked in the past; why waste time reinventing the wheel?
• have a shared vocabulary around software design.
- They allow you to tell a fellow software engineer “I used a Strategy pattern here to allow the algorithm
used to compute this calculation to be customizable”.
- You don’t have to waste time explaining what you mean since you both know the Strategy pattern.
▪ Design patterns provide you not with code reuse but with experience reuse.
• Knowing concepts such as abstraction, inheritance and polymorphism will NOT make you a good designer,
unless you use those concepts to create flexible designs that are maintainable and that can cope with
change.
▪ Design patterns can show you how to apply those concepts to achieve those goals.
Object-Oriented Programming 9
Design Patterns A Sense of Perspective
Object-Oriented Programming 10
Design Patterns Other Advantages
Object-Oriented Programming 11
Design Patterns Original Catalogue of Patterns
Purpose
Creational Structural Behavioral
Abstract Factory Adapter Bridge Chain of Responsibility
Builder Composite Command
Factory Method Decorator Interpreter
Prototype Façade Iterator
Singleton Flyweight Mediator
Proxy Memento
Observer
State
Strategy
Template Method
Visitor
Object-Oriented Programming 12
Design Patterns Original Catalogue of Patterns
Purpose
Creational Structural Behavioral
Abstract Factory Adapter Chain of Responsibility
Builder Bridge Command
Factory Method Composite Interpreter
Prototype Decorator Iterator
Singleton Façade Mediator
Flyweight Memento
Proxy Observer
State
Strategy
Template Method
Visitor
▪ Patterns We will be talking about in detail; you should read about the others in the book
or online.
Object-Oriented Programming 13
Design Patterns Design Pattern by Example
▪ SimUDuck: a “duck pond simulator” that can show a wide variety of duck species
swimming and quacking
• Initial State
▪ But a request has arrived to allow ducks to also fly. (We need to stay ahead of the
competition!)
Object-Oriented Programming 14
Design Pattern by Example Easy
Object-Oriented Programming 15
Design Pattern by Example Whoops!
Object-Oriented Programming 16
Design Pattern by Example Double Whoops!
Object-Oriented Programming 17
Design Pattern by Example What about an Interface?
Object-Oriented Programming 18
Design Pattern by Example Design Trade-offs
▪ Use of abstract base class over an interface? Could do it, but only in languages that
support multiple inheritance
• In this approach, you implement Flyable and Quackable as abstract base classes and then have Duck
subclasses use multiple inheritance
Object-Oriented Programming 19
Design Pattern by Example Design Principles to the Rescue!
Object-Oriented Programming 20
Design Pattern by Example Basic Idea
▪ Take any behavior that varies across Duck subclasses and pull them out of Duck.
• Duck will no longer have fly() and quack() methods directly.
• Create two sets of classes, one that implements fly behaviors and one that implements quack
behaviors.
▪ Code to an Interface.
• We’ll make use of the “code to an interface” principle and make sure that each member of the
two sets implements a particular interface.
- For QuackBehavior, we’ll have Quack, Squeak, MuteQuack
- For FlyBehavior, we’ll have FlyWithWings, FlyNoWay, FlyWhenThrown, …
▪ Additional Benefits.
• Other classes can gain access to these behaviors and we can add additional behaviors without
impacting other classes.
Object-Oriented Programming 21
Design Pattern by Example “Code to Interface” Does NOT Imply Java Interface
▪ When we say “code to an interface” it implies that the object that is using the interface
will have a variable whose type is the supertype (whether it is an interface or an abstract
base class) and thus
• can point at any implementation of that supertype
• and is shielded from their specific class names
- A Duck will point to a fly behavior with a variable of type FlyBehavior NOT FlyWithWings; the code will
be more loosely coupled as a result.
Object-Oriented Programming 22
Design Pattern by Example Bringing it all Together: Delegation
▪ .To take advantage of these new behaviors, we must modify Duck to delegate its flying and
quacking behaviors to these other classes
• rather than implementing this behavior internally.
▪ We’ll add two attributes that store the desired behavior and we’ll rename fly() and
quack() to performFly() and performQuack()
• this last step is meant to address the issue of it not making sense for a DecoyDuck to have
methods like fly() and quack() directly as part of its interface.
- Instead, it inherits these methods and plugs-in CantFly and Silence behaviors to make sure that it does
the right things if those methods are invoked.
Object-Oriented Programming 23
Design Pattern by Example New Class Diagram
<<interface>> <<interface>>
flyBehavior quackBehavior
FlyBehavior QuackBehavior
fly() quack()
Duck
flyBehavior
quackBehavior
swim()
display()
FlyWithWings FlyNoWay MuteQuack Quack Squeak
setFlyBehavior(FlyBehavior)
fly() fly() setQuackBehavior(QuackBehavior) quack() quack() quack()
performFly()
performQuack()
▪ FlyBehavior and QuackBehavior define a set of behaviors that provide behavior to Duck.
▪ Duck delegates to each set of behaviors and can switch among them dynamically, if needed.
▪ While each subclass now has a performFly() and performQuack() method, at least the user interface is
uniform and those methods can point to null behaviors when required.
Object-Oriented Programming 24
Design Pattern by Example FlyBehavior.java and QuackBehavior.java
FlyBehavior.java FlyWithWings.java
FlyRocketPowered.java FlyNoWay.java
1 public class FlyRocketPowered implements FlyBehavior { 1 public class FlyNoWay implements FlyBehavior {
2 2
3 public void fly() { 3 public void fly() {
4 System.out.println("I'm flying with a rocket"); 4 System.out.println("I can't fly");
5 } 5 }
6 6
7 } 7 }
Object-Oriented Programming 25
Design Pattern by Example FlyBehavior.java and QuackBehavior.java
QuackBehavior.java Squeak.java
Quack.java MuteQuack.java
1 public class Quack implements QuackBehavior { 1 public class MuteQuack implements QuackBehavior {
2 2
3 public void quack() { 3 public void quack() {
4 System.out.println("Quack"); 4 System.out.println("<< Silence >>");
5 } 5 }
6 6
7 } 7 }
Object-Oriented Programming 26
Design Pattern by Example Duck.java
Note: code to interface, delegation, encapsulation, and ability to change behaviors dynamically
Object-Oriented Programming 27
Design Pattern by Example RubberDuck.java
MallardDuck.java RedHeadDuck.java
1 public class MallardDuck extends Duck { 1 public class RedHeadDuck extends Duck {
2 2
3 public MallardDuck() { 3 public RedHeadDuck() {
4 4
5 quackBehavior = new Quack(); 5 flyBehavior = new FlyWithWings();
6 flyBehavior = new FlyWithWings(); 6 quackBehavior = new Quack();
7 7
8 } 8 }
9 9
10 public void display() { 10 public void display() {
11 System.out.println("I'm a real 11 System.out.println("I'm a real
12 Mallard duck"); 12 Red Headed duck");
13 } 13 }
14 } 14 }
Object-Oriented Programming 28
Design Pattern by Example RubberDuck.java
RubberDuck.java DecoyDuck.java
1 public class RubberDuck extends Duck { 1 public class DecoyDuck extends Duck {
2 2 public DecoyDuck() {
3 public RubberDuck() { 3
4 flyBehavior = new FlyNoWay(); 4 setFlyBehavior(new FlyNoWay());
5 quackBehavior = new Squeak(); 5 setQuackBehavior(new MuteQuack());
6 } 6
7 7 }
8 public RubberDuck(FlyBehavior flyBehavior, 8
9 QuackBehavior quackBehavior) { 9 public void display() {
10 this.flyBehavior = flyBehavior; 10 System.out.println("I'm a duck Decoy");
11 this.quackBehavior = quackBehavior; 11 }
12 } 12 }
13
14 public void display() {
15 System.out.println("I'm a rubber duckie");
16 }
17 }
Object-Oriented Programming 29
Design Pattern by Example DuckSimulator.java (I)
DuckSimulator.java
1 import java.util.*;
2
3 public class DuckSimulator {
4 public static void main(String[] args) {
5 List<Duck> ducks = new LinkedList<>();
6
7 Duck myDuck = new RubberDuck();
8 Note: all variables are of type Duck, not the
9 ducks.add(new MallardDuck());
10 ducks.add(new DecoyDuck()); specific subtypes; “code to interface” in action
11 ducks.add(new RedheadDuck());
12 ducks.add(myDuck);
13
14 processDucks(ducks);
15
16 // Change my ducks' behavior dynamically to make a rubber
17 // duck that can fly rocket powered and speak
18 myDuck.setFlyBehavior(new FlyRocketPowered());
19 myDuck.setQuackBehavior(new Speak());
Note: here we see the power of delegation.
20 We can change behaviors at run-time
21 processDucks(ducks);
22 }
Object-Oriented Programming 30
Design Pattern by Example DuckSimulator.java (II)
FlyBehavior.java (continue)
1 public static void processDucks(List<Duck> ducks) {
2 for (Duck duck : ducks) {
3 System.out.println("--------------------");
4 System.out.println("Name: " + duck.getClass().getName());
5 duck.display();
6 duck.performQuack();
7 duck.performFly();
8 duck.swim();
9 }
10 Because of abstraction and
11 System.out.println("Done processing ducks\n"); polymorphism, processDucks()
12 } consists of nice, clean, robust,
13 }
and extensible code!
Object-Oriented Programming 31
Design Pattern by Example Not Completely Decoupled
▪ No!
• The subclasses are still coded into DuckSimulator
- Duck myDuck = new RubberDuck();
Object-Oriented Programming 32
Design Patterns Strategy Pattern
Strategy Pattern
Object-Oriented Programming 33
Strategy Pattern Meet the Strategy Design Pattern
▪ The solution that we applied to this design problem is known as the Strategy Design
Pattern
• Definition: The Strategy pattern defines a family of algorithms, encapsulates each one, and
makes them interchangeable. Strategy lets the algorithm vary independently from clients that
use it
Object-Oriented Programming 34
Strategy Pattern Structure of Strategy
▪ Algorithm is pulled out of Host. Client only makes use of the public interface of
Algorithm and is not tied to concrete subclasses.
▪ Client can change its behavior by switching among the various concrete algorithms
Object-Oriented Programming 35
Strategy Pattern Review of Delegate
▪ Purpose of Delegate:
• Allow an object’s behavior to be customized without forcing a developer to create a subclass
that overrides default behavior
▪ Structure
• Host object; Delegate Interface; Delegate object; Client
• Client invokes method on Host; Host checks to see if Delegate handles this method; if so, it
routes the call to the Delegate; if not, it provides default behavior for the method
Object-Oriented Programming 36
Strategy Pattern Structure of Delegate
Object-Oriented Programming 37
Design Patterns Adapter Pattern
Adapter Pattern
Object-Oriented Programming 38
Adapter Pattern Adapters in the Real World
Object-Oriented Programming 39
Adapter Pattern Software Adapters (I)
▪ Pre-condition: You are maintaining an existing system that makes use of a third-party class
library from vendor A
▪ Stimulus: Vendor A goes belly up and corporate policy does not allow you to make use of
an unsupported class library
▪ Response: Vendor B provides a similar class library but its interface is completely different
from the interface provided by vendor A
▪ Assumptions: You don’t want to change your code, and you can’t change vendor B’s code
▪ Solution?: Write new code that adapts vendor B’s interface to the interface expected by
your original code
Object-Oriented Programming 40
Adapter Pattern Software Adapters (II)
Object-Oriented Programming 41
Adapter Pattern Software Adapters (III)
…plug it in
Benefit: Existing system and new vendor library do not change — new code is isolated
within the adapter
Object-Oriented Programming 42
Adapter Pattern Example: A Turkey Amongst Ducks! (I)
▪ If it walks like a duck and quacks like a duck, then it must be a duck!
Or…
Object-Oriented Programming 43
Adapter Pattern Example: A Turkey Amongst Ducks! (II)
MallarDuck.java
1 public class MallardDuck implements Duck {
2 public void quack() {
3 System.out.println("Quack");
4 }
5
6 public void fly() {
7 System.out.println("I'm flying");
8 }
9 }
Object-Oriented Programming 44
Adapter Pattern Example: A Turkey Amongst Ducks! (III)
WildTurkey.java
1 public class WildTurkey implements Turkey {
2 public void gobble() {
3 System.out.println("Gobble Gobble"); But the duck simulator
4 }
5
doesn’t know how to handle
6 public void fly() { turkeys, only ducks!
7 System.out.println("I'm flying a short distance");
8 }
9 }
Object-Oriented Programming 45
Adapter Pattern Example: A Turkey Amongst Ducks! (IV)
Object-Oriented Programming 46
Adapter Pattern DuckSimulator.java
DuckSimulator.java
1 import java.util.LinkedList;
2 import java.util.List;
3
4 public class DuckSimulator {
5 public static void main(String[] args) {
6 MallardDuck mallardDuck = new MallardDuck();
7
8 WildTurkey wildTurkey = new WildTurkey();
9 Duck turkeyAdapter = new TurkeyAdapter(wildTurkey);
10
11 List<Duck> ducks = new LinkedList<Duck>();
12 ducks.add(mallardDuck);
13 ducks.add(turkeyAdapter);
14
15 for (Duck duck : ducks) {
16 duck.quack();
17 duck.fly();
18 }
19 }
20 }
Object-Oriented Programming 47
Adapter Pattern The Adapter Pattern explained
Object-Oriented Programming 48
Adapter Pattern Definition (I)
▪ The Adapter pattern converts the interface of a class into another interface that clients
expect. Adapter lets classes work together that couldn’t otherwise because of
incompatible interfaces.
• The client makes a request on the adapter by invoking a method from the target interface
on it
• The adapter translates that request into one or more calls on the adaptee using the adaptee
interface
• The client receives the results of the call and never knows there is an adapter doing the
translation
Object-Oriented Programming 49
Adapter Pattern Definition (II)
Object-Oriented Programming 50
Adapter Pattern Structure (I)
Object-Oriented Programming 51
Adapter Pattern Structure (II)
Object-Oriented Programming 52
Adapter Pattern Real World Adapters
▪ Before Java’s new collection classes, iteration over a collection occurred via
java.util.Enumeration
• hasMoreElements() : boolean
• nextElement() : Object
▪ With the collection classes, iteration was moved to a new interface: java.util.Iterator
• hasNext(): boolean
• next(): Object
• remove(): void
▪ There’s a lot of code out there that makes use of the Enumeration interface
• New code can still make use of that code by creating an adapter that converts from the Enumeration
interface to the Iteration interface
Object-Oriented Programming 53
Design Patterns Facade Pattern
Facade Pattern
Object-Oriented Programming 54
Façade Pattern Façade (I)
Object-Oriented Programming 55
Façade Pattern Structure
Object-Oriented Programming 56
Façade Pattern Façade (II)
▪ Façade works best when you are accessing a subset of the system’s functionality
• You can add new features by adding them to the Façade (not the subsystem); you still get a
simpler interface
▪ Façade not only reduces the number of methods you are dealing with but also the
number of classes
• Imagine having to pull Employees out of Divisions that come from Companies that you pull from
a Database
- A Façade in this situation can fetch Employees directly
Object-Oriented Programming 57
Façade Pattern Example (Without a Façade)
Object-Oriented Programming 58
Façade Pattern Example (With a Façade)
▪ With a Façade, the Client is shielded from most of the classes. It uses the Database Façade
to retrieve Employee objects directly.
Object-Oriented Programming 59
Façade Pattern Façade Example (I)
Object-Oriented Programming 60
Façade Pattern Façade Example (II)
Object-Oriented Programming 61
Façade Pattern Façade Example (III)
Let’s check out those same tasks in terms of the classes and the method calls needed to
perform them:
Object-Oriented Programming 62
Façade Pattern Façade Example (IV)
Object-Oriented Programming 63
Façade Pattern Façade Example (V)
Object-Oriented Programming 64
Façade Pattern Façade Example (VI)
… in a façade object and encode all of the steps for each high level service in the façade;
▪ Indeed, Façade lets us encapsulate subsystems, hiding them from the rest of the system
Object-Oriented Programming 65
Façade Pattern Façade vs. Adapter Comparison (I)
Object-Oriented Programming 66
Façade Pattern Façade vs. Adapter Comparison (II)
▪ Superficial difference
• Façade hides many classes
• Adapter hides only one
▪ But
• a Façade can simplify a single, very complex object
• an Adapter can wrap multiple objects at once in order to access all the functionality it needs
Object-Oriented Programming 67
Façade Pattern Demonstration: Home Theater
PopcornPopper.java Screen.java
1 public class PopcornPopper { 1 public class Screen {
2 String description; 2 String description;
3
4 3
public PopcornPopper(String description) {
5 this.description = description; 4 public Screen(String description) {
6 } 5 this.description = description;
7 6 }
8 public void on() { 7
9 System.out.println(description + " on"); 8 public void up() {
10 }
11 9 System.out.println(description + " going up");
12 public void off() { 10 }
13 System.out.println(description + " off"); 11
14 } 12 public void down() {
15 13 System.out.println(description + " going down");
16 public void pop() { 14 }
17 System.out.println(description + " popping popcorn!");
18 15
}
19 16 public String toString() {
20 public String toString() { 17 return description;
21 return description; 18 }
22 } 19 }
23 }
Object-Oriented Programming 68
Façade Pattern Demonstration: Home Theater
Projector.java TheaterLights.java
1 public class Projector { 1 public class TheaterLights {
2 String description; 2 String description;
3 DvdPlayer dvdPlayer;
4 3
5 public Projector(String description, DvdPlayer dvdPlayer) { 4 public TheaterLights(String description) {
6 this.description = description; 5 this.description = description;
7 this.dvdPlayer = dvdPlayer; 6 }
8 } 7
9
10 8 public void on() {
public void on() {
11 System.out.println(description + " on"); 9 System.out.println(description + " on");
12 } 10 }
13 11
14 public void off() { 12 public void off() {
15 System.out.println(description + " off");
16 13 System.out.println(description + " off");
}
17 14 }
18 public void wideScreenMode() { 15
19 System.out.println(description + " in widescreen mode (16x9 aspect ratio)"); 16 public void dim(int level) {
20 } 17 System.out.println(description + " dimming to "
21
22 18 + level + "%");
public void tvMode() {
23 System.out.println(description + " in tv mode (4x3 aspect ratio)"); 19 }
24 } 20
25 21 public String toString() {
26 public String toString() { 22 return description;
27 return description;
28 23 }
}
29 } 24 }
Object-Oriented Programming 69
Façade Pattern Demonstration: Home Theater
Object-Oriented Programming 70
Façade Pattern Demonstration: Home Theater
WeatherStation.java
1 public class HomeTheaterTestDrive {
2
3 public static void main(String[] args) {
4 Amplifier amp = new Amplifier("Top-O-Line Amplifier");
5 Tuner tuner = new Tuner("Top-O-Line AM/FM Tuner", amp);
6 DvdPlayer dvd = new DvdPlayer("Top-O-Line DVD Player", amp);
7 CdPlayer cd = new CdPlayer("Top-O-Line CD Player", amp);
8 Projector projector = new Projector("Top-O-Line Projector", dvd);
9 TheaterLights lights = new TheaterLights("Theater Ceiling Lights");
10 Screen screen = new Screen("Theater Screen");
11 PopcornPopper popper = new PopcornPopper("Popcorn Popper");
12
13 HomeTheaterFacade homeTheater = new HomeTheaterFacade(
14 amp, tuner, dvd, cd, projector, screen, lights, popper);
15
16 homeTheater.watchMovie("Raiders of the Lost Ark");
17 homeTheater.endMovie();
18 }
19 }
Object-Oriented Programming 71
Design Patterns Observer Pattern
Observer Pattern
Object-Oriented Programming 72
Observer Pattern
▪ Don’t miss out when something interesting (in your system) happens!
• The observer pattern allows objects to keep other objects informed about events
occurring within a software system (or across multiple systems)
• It’s dynamic in that an object can choose to receive or not receive notifications at run-
time
• Observer happens to be one of the most heavily used patterns in the Java Development
Kit
- And indeed is present in many frameworks
Object-Oriented Programming 73
Observer Pattern Weather Monitoring Example
▪ We need to pull information from the station and then generate “current conditions,
weather stats, and a weather forecast”.
Object-Oriented Programming 74
Observer Pattern WeatherData Skeleton
▪ They provide three getter methods for the sensor values and an empty
measurementsChanged() method that is guaranteed to be called whenever a sensor
provides a new value
Object-Oriented Programming 75
Observer Pattern First pass at measurementsChanged
WeatherData.java Problems?
...
public void measurementsChanged() {
float temp = getTemperature(); 1. The number and type of displays
float humidity = getHumidity(); may vary.
float pressure = getPressure();
These three displays are hard coded
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure); with no easy way to update them.
forecastDisplay.update(temp, humidity, pressure);
}
...
2. Coding to implementations, not
an interface!
Each implementation has adopted
the same interface, so this will make
translation easy!
Object-Oriented Programming 76
Observer Pattern Observer Pattern example
• Observer is just like this but we call the publisher the “subject” and we refer to subscribers as
“observers”
Object-Oriented Programming 77
Observer Pattern Observer in Action (I)
Object-Oriented Programming 78
Observer Pattern Observer in Action (II)
Object-Oriented Programming 79
Observer Pattern Observer in Action (III)
Object-Oriented Programming 80
Observer Pattern Observer in Action (IV)
Object-Oriented Programming 81
Observer Pattern Definition and Structure
▪ The Observer Pattern defines a one-to-many dependency between a set of objects, such
that when one object (the subject) changes all of its dependents (observers) are notified
and updated automatically
Object-Oriented Programming 82
Observer Pattern Observer Benefits
Object-Oriented Programming 83
Observer Pattern Demonstration: Weather Monitoring
Subject.java
1 public interface Subject {
2 public void registerObserver(Observer o);
3 public void removeObserver(Observer o);
4 public void notifyObservers();
5 }
Observer.java
1 public interface Observer {
2 public void update(float temp, float humidity, float pressure);
3 }
DisplayElement.java
1 public interface DisplayElement {
2 public void display();
3 }
Object-Oriented Programming 84
Observer Pattern Demonstration: Weather Monitoring
Object-Oriented Programming 85
Observer Pattern Demonstration: Weather Monitoring
ForecastDisplay.java StatisticsDisplay.java
1 import java.util.*; 1 public class StatisticsDisplay implements Observer, DisplayElement {
2 2 private float maxTemp = 0.0f;
3 public class ForecastDisplay implements Observer, DisplayElement { 3 private float minTemp = 200;
4 private float currentPressure = 29.92f; 4 private float tempSum= 0.0f;
5 private float lastPressure; 5 private int numReadings;
6 private WeatherData weatherData; 6 private WeatherData weatherData;
7 7
8 public ForecastDisplay(WeatherData weatherData) { 8 public StatisticsDisplay(WeatherData weatherData) {
9 this.weatherData = weatherData; 9 this.weatherData = weatherData;
10 weatherData.registerObserver(this); 10 weatherData.registerObserver(this);
11 } 11 }
12 12
13 public void update(float temp, float humidity, float pressure) { 13 public void update(float temp, float humidity, float pressure) {
14 lastPressure = currentPressure; 14 tempSum += temp;
15 currentPressure = pressure; 15 numReadings++;
16 16
17 display(); 17 if (temp > maxTemp) {
18 } 18 maxTemp = temp;
19 19 }
20 public void display() { 20 if (temp < minTemp) {
21 System.out.print("Forecast: "); 21 minTemp = temp;
22 if (currentPressure > lastPressure) { 22 }
23 System.out.println("Improving weather on the way!"); 23 display();
24 } else if (currentPressure == lastPressure) { 24 }
25 System.out.println("More of the same"); 25
26 } else if (currentPressure < lastPressure) { 26 public void display() {
27 System.out.println("Watch out for cooler, rainy weather"); 27 System.out.println("Avg/Max/Min temperature = "
28 } 28 + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);
29 } 29 }
30 } 30 }
Object-Oriented Programming 86
Observer Pattern Demonstration: Weather Monitoring
WeatherStation.java
1 import java.util.*;
2
3 public class WeatherStation {
4
5 public static void main(String[] args) {
6 WeatherData weatherData = new WeatherData();
7
8 CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
9 StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
10 ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
11
12 System.out.println("----------------------------------------");
13 weatherData.setMeasurements(80, 65, 30.4f);
14 System.out.println("----------------------------------------");
15 weatherData.setMeasurements(82, 70, 29.2f);
16 System.out.println("----------------------------------------");
17 weatherData.setMeasurements(78, 90, 29.2f);
18 System.out.println("----------------------------------------");
19 }
20 }
Object-Oriented Programming 87
Design Patterns
Factory Pattern
Object-Oriented Programming 88
Factory Pattern Factory Patterns: The Problem with “new”
▪ Each time we invoke the “new” command to create a new object, we violate the “code to
an Interface” design principle
▪ Example
Duck duck = new DecoyDuck();
▪ Even though our variable uses an “interface”, this code depends on “DecoyDuck”
▪ In addition, if you have code that instantiates a particular subtype based on the current
state of the program, then the code depends on each concrete class
Obvious problems:
if (hunting) {
▪ needs to be recompiled each time a dependent changes;
return new DecoyDuck()
▪ add new classes -> change this code
} else {
▪ remove existing classes -> change this code
return new RubberDuck()
} This means that this code violates the open-closed principle
and the “encapsulate what varies” design principle
Object-Oriented Programming 89
Factory Pattern PizzaStore Example
Object-Oriented Programming 90
Factory Pattern Encapsulate Creation Code
Object-Oriented Programming 91
Factory Pattern Factory
Object-Oriented Programming 92
Factory Pattern Demonstration
Object-Oriented Programming 93
Factory Pattern Demonstration
PizzaTestDrive.java
1 public class PizzaTestDrive {
2
3 public static void main(String[] args) {
4 System.out.println("");
5 SimplePizzaFactory factory = new SimplePizzaFactory();
6 PizzaStore store = new PizzaStore(factory);
7
8 Pizza pizza = store.orderPizza("cheese");
9 System.out.println("We ordered a " + pizza.getName() + "\n");
10
11 pizza = store.orderPizza("veggie");
12 System.out.println("We ordered a " + pizza.getName() + "\n");
13 }
14 }
Object-Oriented Programming 94
Factory Pattern Class Diagram of New Solution
While this is nice, it is not as flexible as it can be: to increase flexibility we need to look at two
design patterns: Factory Method and Abstract Factory
Object-Oriented Programming 95
Factory Method Pattern Factory Method
▪ To demonstrate the Factory Method pattern, One franchise wants a factory that
the pizza store example evolves makes NY-style pizzas: thin crust, tasty
sauce, and just a little cheese.
• to include the notion of different
franchises
• that exist in different parts of the country
(California, New York, Chicago)
Object-Oriented Programming 96
Factory Method Pattern Factory Method
▪ What we’re going to do is put the createPizza() method back into PizzaStore, but this time
as an abstract method
• PizzaStore becomes an abstract class with an abstract createPizza() method
• We then create subclasses that override createPizza() for each region
Object-Oriented Programming 97
Factory Method Pattern New PizzaStore Class
Object-Oriented Programming 98
Factory Method Pattern New York Pizza Store
Object-Oriented Programming 99
Factory Method Pattern Chicago Pizza Store
NYStylePepperoniPizza.java ChicagoStylePepperoniPizza.java
1 public class NYStylePepperoniPizza extends Pizza { 1 public class ChicagoStylePepperoniPizza extends Pizza {
2 2
3 public NYStylePepperoniPizza() { 3 public ChicagoStylePepperoniPizza() {
4 name = "NY Style Pepperoni Pizza"; 4 name = "Chicago Style Pepperoni Pizza";
5 dough = "Thin Crust Dough"; 5 dough = "Extra Thick Crust Dough";
6 sauce = "Marinara Sauce"; 6 sauce = "Plum Tomato Sauce";
7 7
8 toppings.add("Grated Reggiano Cheese"); 8 toppings.add("Shredded Mozzarella Cheese");
9 toppings.add("Sliced Pepperoni"); 9 toppings.add("Black Olives");
10 toppings.add("Garlic"); 10 toppings.add("Spinach");
11 toppings.add("Onion"); 11 toppings.add("Eggplant");
12 toppings.add("Mushrooms"); 12 toppings.add("Sliced Pepperoni");
13 toppings.add("Red Pepper"); 13 }
14 } 14
15 15 void cut() {
16 void cut() { 16 System.out.println("Cutting the pizza
17 System.out.println("Cutting the pizza 17 into square slices");
18 into 8 slices"); 18 }
19 } 19 }
20 }
The Factory Method design pattern defines an interface for creating an object, but lets subclasses
decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Factory Method leads to the creation of parallel class hierarchies; ConcreateCreators produce
instances of ConcreteProducts that are operated on by Creators via the Product interface
▪ In this design, PizzaStore (the high-level class) no longer depends on the Pizza subclasses
(the low level classes); they both depend on the abstraction “Pizza”.
▪ To achieve the dependency inversion principle in your own designs, follow these
GUIDELINES
• No variable should hold a reference to a concrete class
• No class should derive from a concrete class
• No method should override an implemented method of its base classes
▪ These are “guidelines” because if you were to blindly follow these instructions, you would
never produce a system that could be compiled or executed
• Instead use them as instructions to help optimize your design
▪ And remember, not only should low-level classes depend on abstractions, but high-level
classes should to… this is the very embodiment of “code to an interface”
• The FactoryMethod directory of this lecture’s example source code contains an implementation
of the pizza store using the factory method design pattern
- It even includes a file called “DependentPizzaStore.java” that shows how the code would be
implemented without using this pattern
▪ The factory method approach to the pizza store is a big success, allowing our company to
create multiple franchises across the country quickly and easily
• Our company’s success has always been dependent on the use of fresh, quality ingredients
- So, something must be done!
▪ We will alter our design such that a factory is used to supply the ingredients that are
needed during the pizza creation process
• Since different regions use different types of ingredients, we’ll create region-specific subclasses
of the ingredient factory to ensure that the right ingredients are used
• But, even with region-specific requirements, since we are supplying the factories, we’ll make
sure that ingredients that meet our quality standards are used by all franchises
- They’ll have to come up with some other way to lower costs.
PizzaIngredientFactory.java
Pizza.java
1 public abstract class Pizza {
2 String name;
3
4 Dough dough;
5 Sauce sauce;
6 Veggies veggies[]; First, alter the Pizza abstract base class to
7 Cheese cheese; make the prepare method abstract…
8 Pepperoni pepperoni;
9 Clams clam;
10
11 abstract void prepare();
12
13 void bake() {
14 System.out.println("Bake for 25 minutes at 350");
15 }
16
17 void cut() {
18 System.out.println("Cutting the pizza into diagonal slices");
19 }
CheesePizza.java
1 public class CheesePizza extends Pizza {
2 PizzaIngredientFactory ingredientFactory;
3
4 public CheesePizza(PizzaIngredientFactory ingredientFactory) {
5 this.ingredientFactory = ingredientFactory;
6 }
7
8 void prepare() {
9 System.out.println("Preparing " + name);
10 dough = ingredientFactory.createDough(); Then, update pizza subclasses to make
11 sauce = ingredientFactory.createSauce(); use of the factory!
12 cheese = ingredientFactory.createCheese();
13 } Note: we no longer need subclasses like
14 } NYCheesePizza and ChicagoCheesePizza
because the ingredient factory now
handles regional differences
ChicagoPizzaStore.java
1 public class ChicagoPizzaStore extends PizzaStore {
2
3 protected Pizza createPizza(String item) {
4 Pizza pizza = null;
5 PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();
6
7 if (item.equals("cheese")) {
8 pizza = new CheesePizza(ingredientFactory);
9 pizza.setName("Chicago Style Cheese Pizza");
10 } else if (item.equals("veggie")) {
11 pizza = new VeggiePizza(ingredientFactory);
12 pizza.setName("Chicago Style Veggie Pizza");
13 } We need to update our PizzaStore
14 . . . subclasses to create the appropriate
15 } ingredient factory and pass it to each
16
17 return pizza; Pizza subclass within the createPizza
18 } method
19 }
▪ This abstract factory gives us an interface for creating a family of products (e.g., NY pizzas,
Chicago pizzas)
• The factory interface decouples the client code from the actual factory implementations that
produce context-specific sets of products
▪ Our client code (PizzaStore) can then pick the factory appropriate to its region, plug it in,
and get the correct style of pizza (Factory Method) with the correct set of ingredients
(Abstract Factory)
Note:
Lots of classes not shown
▪ The Abstract Factory design pattern provides an interface for creating families of related or
dependent objects without specifying their concrete classes
▪ Consider an example in which we have recipes for making tea and coffee at a coffee shop
▪ Coffee
• Boil water
• Brew coffee in boiling water
• Pour coffee in cup
• Add sugar and milk
▪ Tea
• Boil water
• Steep tea in boiling water
• Pour tea in cup
• Add lemon
Coffee.java
1 public class Coffee {
2 void prepareRecipe() { Here’s our recipe for coffee, straight
3 boilWater(); out of the training manual.
4 brewCoffeeGrinds();
5 pourInCup();
6 addSugarAndMilk(); Each of the steps is implemented as
7 }
8 a separate method.
9 public void boilWater() {
10 System.out.println("Boiling water");
11 }
12
13 public void brewCoffeeGrinds() {
14 System.out.println("Dripping Coffee through filter"); Each of these methods implements
15 }
16 one step of the algorithm. There’s a
17 public void pourInCup() { method to boil water, brew the
18 System.out.println("Pouring into cup");
19 }
coffee, pour the coffee in a cup and
20 add sugar and milk.
21 public void addSugarAndMilk() {
22 System.out.println("Adding Sugar and Milk");
23 }
24 }
Tea.java
1 public class Tea {
2 void prepareRecipe() {
3 boilWater();
4 steepTeaBag(); This looks very similar to the one we
5 pourInCup(); just implemented in Coffee; the
6 addLemon(); second and forth steps are different,
7 }
8 but it’s basically the same recipe.
9 public void boilWater() {
10 System.out.println("Boiling water");
11 }
12
13 public void steepTeaBag() { Notice that these two methods
14 System.out.println("Steeping the tea"); These two are exactly the same as they
15 }
16 methods are are in Coffee! So we definitely
17 public void addLemon() { specialized to Tea. have some code duplication
18 System.out.println("Adding Lemon"); going on here.
19 }
20
21 public void pourInCup() {
22 System.out.println("Pouring into cup");
23 }
24 }
▪ The structure of the algorithms in prepareRecipe() is similar for Tea and Coffee
• We can improve our code further by making the code in prepareRecipe() more abstract
- brewCoffeeGrinds() and steepTea() -> brew()
- addSugarAndMilk() and addLemon() -> addCondiments()
CaffeineBeverage.java
1 public abstract class CaffeineBeverage {
2 Because Coffee and Tea handle these
3 final void prepareRecipe() {
4 boilWater(); methods in different ways, they’re going
5 brew(); to have to be declared as abstract. Let
6 pourInCup();
7 addCondiments(); the subclasses worry about that stuff!
8 }
9
10 abstract void brew();
11 abstract void addCondiments(); brew() and addCondiments() are
12 abstract and must be supplied by subclasses
13 void boilWater() {
14 System.out.println("Boiling water");
15 } boilWater() and pourInCup() are
16 specified and shared across all subclasses
17 void pourInCup() {
18 System.out.println("Pouring into cup");
19 }
20 }
Coffee.java
▪ Same for Coffee, except Coffee
1 public class Coffee extends CaffeineBeverage {
2 public void brew() {
deals with coffee, and sugar and
3 System.out.println("Dripping Coffee through filter"); milk instead of tea bags and
4 }
5 lemon.
6 public void addCondiments() {
7 System.out.println("Adding Sugar and Milk");
8 }
9 }
▪ Made steps of algorithm more abstract and specified its structure in the superclass
• Thereby eliminating another “implicit” duplication between the two classes
▪ The Template Method pattern defines the skeleton of an algorithm in a method, deferring
some steps to subclasses. Template Method lets subclasses redefine certain steps of an
algorithm without changing the algorithm’s structure
• Template Method defines the steps of an algorithm and allows subclasses to provide the
implementation for one or more steps
Singleton Pattern
Ball.java BallTestDrive.java
1 public class Ball { 1 public class BallTestDrive {
2 private String color; 2 public static void main(String[] args) {
3 3 Ball b1 = new Ball("Red");
4 private Ball(String color) { 4 }
5 this.color = color; 5 }
6 }
7
8 public void bounce() {
9 System.out.println("Boing!");
10 }
Now
11 } Ball b1 = new Ball("Red");
impossible by any method outside of Ball
▪ Now that the constructor is private, no class can gain access to instances of Ball
• But our requirements were that there would be at least one way to get access to an instance of
Ball
Ball.java BallTestDrive.java
1 public class Ball { 1 public class BallTestDrive {
2 private String color; 2
3 3 public static void main(String[] args) {
4 private Ball(String color) { 4 // Ball b1 = new Ball("Red");
5 this.color = color; 5 Ball b1 = Ball.getInstance("Blue");
6 } 6 }
7
7 }
8 public void bounce() {
9 System.out.println("Boing!");
10 }
11 We are back to this problem where any client
12 public static Ball getInstance(String color) { can create an instance of Ball;
13 return new Ball(color); instead of saying this:
14 } Ball b1 = new Ball("Red");
15
16 } they just say:
Ball b1 = Ball.getInstance("Blue");
▪ The solution to the final problem is to change the private static instance variable to a Map
private static Map<String, Ball> ballRecord = new HashMap...
▪ Then check if the map contains an instance for a given value of the parameter
• this ensures that only one ball of a given color is ever created
• this is a very acceptable variation of the Singleton pattern
Ball.java
1 public static Ball getInstance(String color) {
2 if (!ballRecord.containsKey(color)) {
3 ballRecord.put(color, new Ball(color));
4 }
5
6 return ballRecord.get(color);
7 }
▪ Used to ensure that only one instance of a particular class ever gets created and that
there is just one (global) way to gain access to that instance
• What’s really going on here? We’re taking a class and letting it manage a single instance of itself.
We’re also preventing any other class from creating a new instance on its own. To get an
instance, you’ve got to go through the class itself.
• We’re also providing a global access point to the instance: whenever you need an instance, just
query the class and it will hand you back the single instance. We can implement this so that the
Singleton is created in a lazy manner, which is especially important for resource-intensive
objects.
▪ Singleton involves only a single class (not typically called Singleton). That class is a full-
fledged class with other attributes and methods (not shown).
▪ The class has a static variable that points at a single instance of the class.
▪ The class has a private constructor (to prevent other code from instantiating the class)
and a static method that provides access to the single instance.
▪ Logger classes
▪ Factories
• Especially those that issue IDs
• Singleton is often combined with Factory Method and Abstract Factory patterns
Decorator Pattern
▪ The Decorator Pattern provides a powerful mechanism for adding new behaviors to an
object at run-time
• The mechanism is based on the notion of “wrapping” which is just a fancy way of saying “delegation” but
with the added twist that the delegator and the delegate both implement the same interface
▪ The decorator pattern provides yet another way in which a class’s runtime behavior can
be extended without requiring modification to the class
- Inheritance is one way to do this, but composition and delegation are more flexible (and Decorator takes
advantage of delegation)
- As the Gang of Four put it: “Decorator lets you attach additional responsibilities to an object dynamically.
Decorators provide a flexible alternative to subclassing for extending functionality.”
• Our “Starbuzz Coffee” example, taken from Head First Design Patterns, clearly demonstrates why
inheritance can get you into trouble and why delegation/composition provides greater run-time flexibility
▪ Under pressure to update their “point of sale” system to keep up with their expanding set
of beverage products
• Started with a Beverage abstract base class and four implementations: HouseBlend, DarkRoast, Decaf,
and Espresso
- Each beverage can provide a description and compute its cost
• But they also offer a range of condiments including: steamed milk, soy, and mocha
- The use of the word “Alter” here is key since it provides a hint that we might be able to use the Decorator pattern
▪ With inheritance on your brain, you may add condiments to this design in one of two ways
1. One subclass per combination of condiment (wont work in general but especially not in Boulder!)
2. Add condiment handling to the Beverage superclass
Each decorator is
cohesive, focusing
just on its added
functionality
▪ At run-time, concrete decorators wrap
concrete components and/or other
concrete decorators
Beverage acts as
our abstract
component class.
Here's the reference to the
Beverage that the
Decorators will be rapping.
FilterInputStream is an
abstract decorator.
▪ As we saw, Decorator offers another solution to the problem of rapidly multiplying combinations
of subclasses
• we saw examples of other solutions when we made use of the strategy pattern and the bridge pattern
▪ The decorator pattern provides a means for creating different combinations of functionality by
creating chains in which each member of the chain can augment or “decorate” the output of the
previous member
• Plus, it separates the step of building these chains from the use of these chains
▪ The Decorator pattern comes into play when there are a variety of optional functions that can
precede or follow another function that is always executed
• Plus, it separates the step of building these chains from the use of these chains
▪ Patterns
• http://en.wikipedia.org/wiki/Software_pattern
• https://sourcemaking.com/design_patterns
• http://hillside.net/patterns/patterns-catalog
• http://c2.com/ppr/
▪ Anti-Patterns
• http://en.wikipedia.org/wiki/Anti-pattern#Programming_anti-patterns