Common Design Patterns

Common Design Patterns

ExerciseFiles
https://github.com/chinhuy/design_pattern.git
Tags
Design Pattern
OOP
Design Principle
Published

Introduction

Don’t reinvent the wheel

  • Design Patterns are general solutions to common object-oriented problems
  • Design Patterns help to create software more flexible, maintainable, and resilient to change or even just easier to communicate with your teammates.

What you should know

  • UML
  • Java

Design Patterns

Object-oriented design experience

  • Object-oriented design experiences don’t come easy. It can take a lot of trial and error to come up with designs that are flexible and extensible
  • Your design can quickly go off the rail if you only have the core OOP concepts (Encapsulate, Abstraction, Inheritance, Polymorphism) in your toolbox
  • Design Pattern = Design Insight + Object-Oriented Design Experience

What are design patterns?

  • Design Patterns are object-oriented design experiences
  • Design Patterns are solutions for common problems
  • Design Patterns are not algorithms
  • Design Patterns are not coding
 
Creational Patterns
Structural Patterns
Behavioral Patterns
Abstract Factory
Adapter
Chain of Responsibility
Builder
Bridge
Command
Factory Method
Composite
Interpreter
Prototype
Decorator
Iterator
Singleton
Facade
Mediator
Flyweight
Memento
Proxy
Observer
State
Strategy
Template
Visitor
  • There are 23 original design patterns in the Gang of Four catalogs.
  • They are grouped into 3 categories: Creational, Structural, Behavioral
  • We will focus on 6 common design patterns
    • Factory Method
    • Adapter
    • Decorator
    • Iterator
    • Observer
    • Strategy
 

Design Principles vs Design Patterns

Design Principles vs. Design Patterns
notion image
Design Principles
  • Encapsulate what varies
  • Favor composition over inheritance
  • Loop coupling
  • Program to interface
  • Single Responsibility
  • Open Closed
  • Liskov substitution
  • Interface segregation
  • Dependency Inversion
 

Strategy Pattern

Problem

notion image
  • Assume we need to model a Duck object which it can fly, quack, swim, and display
  • Duck can be a mallard duck or a red head duck
  • To design this system, we use inheritance here
  • And Everything works fine
  • But we have another request that we need to support a rubber duck as well
notion image
  • Think about the request, we found that just adding another subclass RubberDuck and extending Duck.
  • But a rubber duck can not fly so we need to override fly method
  • Also, a rubber duck will squake instead of quacking. So, we need to override quack method as well.
  • How about if we need to support a decoy duck?
notion image
  • We found that a decoy duck can not fly. So, we need to override fly method
  • It can not quack, so we need to override quack method
  • How about if we have to hundred of ducks like that?
  • It seems we have a problem with this design, right?
Problems with this design using inheritance
  • Code duplicated, we need to override a lot of methods from the base class
  • Explode subclasses
How about if we use interfaces
notion image
  • We found that there are some ducks that could not fly or quack
  • We can separate fly() and quack() into interfaces
  • MalladDuck and RedheadDuck will implement both interfaces
  • RubberDuck only needs to implement Quackable
  • This design is better but code is not re-used
  • How about if we have hundreds of Duck classes and they are able fly and quack, we have to implement all them.
  • How about using has-a instead of is-a and program to interface?
notion image
  • Now, Duck HAS-A a FlyBehavior and a FquackBehavior and It depends on an abstraction (program to the interface)
  • We can define all kinds of FlyBehavior and QuackBehavior
  • Each Duck can set a specific behavior at run-time
  • Now, Duck can performFly and performQuack by delegating it to FlyBehavior and QuackBehavior

Problem 2

notion image
  • Currently, we support to share a photo via text message and via email
  • We have another to share photo via social network
notion image
  • Create an interface ShareBehavior
  • Create TextSharing class, EmailSharing class, and SocialNetworkSharing class. All those 3 classes will implement ShareBehavior
  • Create HAS-A relationship between CameraApp and ShareBehavior interface.

Strategy Pattern

notion image
This pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. This lets the algorithm vary independently from clients that use it

Adapter Pattern

Understanding the adapter pattern

notion image
  • How can we plug an American-style electrical cord into a European-style outlet?
  • It seems we can’t because they have different interfaces
notion image
  • To make it work we have adapter to adapt The European style outlet into the American-style plug
In computer science
notion image
  • Assume that Your System working well with Vendor Class
  • At some points, you want to use a different Vendor Class. Maybe the new Vendor Class is cheaper or better.
  • The problem is that the new Vendor class has a different interface with your existing system.
  • So, how to make it work?
notion image
  • We use an adapter

The Adapter pattern defined

notion image
The pattern converts the interface of a class into another interface that clients expect. It allows classes to work together that could not otherwise because of incompatible interfaces
How it work?
notion image
  • The client makes a request as usual
  • The adapter will translate that request into a request which Adaptee can understand.
  • When the Adaptee responds to the result, it will translate it back to the client.

Using the Adapter pattern

notion image
  • Assume we have DuckSimulator which is working with Duck interface with 2 methods quack() and fly()
  • How do we make it work with Turkey interface with 2 methods gobble() and fly()
Adapters Use Composition
  • The client is composed with the class with the target interface
  • The adapter is composed of the adaptee
  • The adapter delegates calls to the adaptee and return any needed value
  • The client and the adaptee don’t know there’s an adapter in between
 

Challenge

public interface Drone { public void beep(); public void sprin_rotors(); public void take_off(); } public class SuperDrone implements Drone { public void beep() { System.out.println("Beep beep beep"); } public void spin_rotors() { System.out.println("Rotors are spinning"); } public void take_off() { System.out.println("Taking off"); } }
  • Create adapter to adapt a Drone to a Duck
 

Solution

notion image
//DroneAdapter class public class DroneAdapter implements Duck{ private Drone drone; public DroneAdapter(Drone drone){ this.drone = drone; } @Override public void fly() { drone.spin_rotor(); drone.take_off(); } @Override public void quack() { drone.beep(); } } //DuckSimulator class Drone drone = new SupperDrone(); DroneAdapter droneAdapter = new DroneAdapter(drone); testDuck(droneAdapter);

Observer Pattern

Understanding the Observer pattern

notion image
  • A publisher who publishes magazines
  • Subscribers who subscribe with the publisher to receive magazines if there are any magazines published
  • A subscriber can unsubscribe at any time to stop receiving the published magazines
notion image
  • Assume we have a Publisher object
  • Any objects can subscribe to be Subscribers
  • Obviously, there’ll be objects that aren’t subscribers
  • And any subscribers can send a request to the publisher to be non-subscribers
  • The publisher typically holds some data of interest. That could be a stock quote or weather temperature,…
notion image
  • And when the data changes, all subscribers are notified

The Observer pattern defined

notion image
This pattern defined a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

Using the Observer pattern

notion image
//SimpleObserver public class SimpleObserver implements Observer{ private Subject subject; private int value; public SimpleObserver(Subject subject){ this.subject = subject; System.out.println("Register observer " + this.toString()); this.subject.registerObserver(this); } @Override public void update(int value) { this.value = value; display(); } public void display(){ System.out.println("The updated value of " + this.toString() + " is " + value); } } //SimpleSubject public class SimpleSubject implements Subject{ private ArrayList<Observer> observers; private int value; public SimpleSubject(){ this.observers = new ArrayList<>(); } public void setValue(int value){ this.value = value; notifyObservers(); } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(value); } } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { if(observers.indexOf(observer) != -1){ observers.remove(observer); } } }

The Observer pattern and loose coupling

Loose Coupling
  • Subjects and observers are loosely coupled
  • They interact but have little knowledge of each other
  • The subject knows only that the observer implements a specific interface
  • The subject doesn’t need to know the concrete class of the observers
  • The subject relies on a list of observers
  • Observers can be added, removed, or replaced at any time

Challenge

notion image
  • The weather station has a set of sensors that are used to measure temperature, wind speed, and pressure.
  • The user interface, Logger, and AlertSystem are all interested in the data and want to be notified when the weather station gets new data from the sensors.
 

Solution

notion image

The Decorator Pattern

Problem

notion image
  • Assume we need to build an order system for a coffee shop that will serve and take payments for beverages
  • The shop has 4 main types of coffee on the menu. Each of which has a description and a cost
  • And for each beverage, you can add a number of condiments like soy, milk, whip, or mocha.
  • Each of these condiments has a small cost which needs to be added to the cost of the coffee.
Design
notion image
  • We create an abstract Beverage class
  • And 4 main types of coffee HouseBlend, DarkRoast, Decaf, Espresso will extend Beverage class and override cost method
  • This design looks nice so far
  • How about Condiments? We have a lot of variants of Condiments
notion image
  • One way to do that, we can use subclasses to add Condiments
  • But there are a lot of Condiments and there will be a lot of sub-classes for these kinds of combinations
notion image
  • This design is simpler than the one using sub-classes
  • But what about if the price of Condiments changes?
  • Or if we requested to add more Condiments?
  • It seems we need to have to open the class for modification and we could not support double condiments
  • This design is not flexible and maintainable

Extending behavior with composition

notion image
  • How about if we add some attributes in the superclass to track Condiments?
//main HouseBlend drink = new HouseBlend() drink.setSoy(); drink.setWhip(); float cost = drink.cost() //HouseBlend class public float cost() { float cost = super.cost(); if (hasMilk()) { cost += .10; } if (hasSoy()) { cost += .20; } if (hasWhip()) { cost += .10; } if (hasMocha()) { cost += .15; } return cost; }
 
 
 
 
 
  • First, we create a DarkRoast object
  • Then we create Mocha object, and wrap DarkRoast object inside it
  • And we can do the same for Whip
  • So, the cost of DarkRoast with Mocha and with Whip will be 0.99 + 0.20 + 0.10
  • This design is very flexible. We can add a number of condiments we want for a beverage
 

Understanding the Decorate pattern

notion image
This pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
The coffee shop design with Decorator pattern
notion image

Using the Decorator pattern

Starbuzz Coffee
notion image
//Beverage abstract class package src.patterns.decorator; public abstract class Beverage { protected String description; public abstract float cost(); public String getDescription(){ return description; } } //HouseBlend class package src.patterns.decorator; public class HouseBlend extends Beverage{ public HouseBlend(){ this.description = "HouseBlend coffee"; } @Override public float cost() { return 1.5f; } } //CondimentDecorator class package src.patterns.decorator; public abstract class CondimentDecorator extends Beverage{ public abstract String getDescription(); } //Milk package src.patterns.decorator; public class Milk extends CondimentDecorator{ private Beverage beverage; public Milk(Beverage beverage){ this.beverage = beverage; this.description = this.beverage.getDescription() + " with Milk"; } @Override public float cost() { return this.beverage.cost() + 0.10f; } @Override public String getDescription() { return description; } } //Starbuzz Coffee package src.patterns.decorator; public class StarbuzzCoffee { public static void main(String[] args) { Beverage beverage = new HouseBlend(); beverage = new Milk(beverage); beverage = new Mocha(beverage); beverage = new Soy(beverage); System.out.println("Your order:" + beverage.getDescription()); System.out.println("Your bill:" + beverage.cost()); } }

Challenge

notion image
  • Design a system using Decorator pattern for the pizza store

Solution

notion image

The Iterator Pattern

Encapsulate iteration

Array of objects
ArrayList in Java
String[] menuItems = new String[MAX_ITEMS]; menuItem[0] = "Regular Pancake Breakfast"; menuItem[1] = "Blueberry Pancakes"; public void print(items) { for(int i; i < items.length; i++) { String item = items[i]; System.out.println(item); } }
ArrayList<String> menuItems = new ArrayList<String>(); menuItems.add("Regular Pancake Breakfast"); menuItems.add("Blueberry Pancakes"); public void print(items) { for(int i; i < items.size; i++) { String item = items.get(i); System.out.println(item); } }
  • The way we iterate through array and ArrayList is different.
  • Do we have a way to iterate through objects so that we don’t need to change the code for each kind of collection of objects?
  • That is the iterator pattern

Understanding the Iterator pattern

This pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation
Aggregate Objects
  • Array
  • Java Collection classes, like ArrayList
  • Lists
  • Maps
  • Sets
  • Dictionaries
notion image
To Iterate over an Aggregate Object
  1. Ask the object for its iterator
  1. Use the iterator to iterate through the items in the aggregate
  1. Iteration code now works with any kind of aggregate object

Using the Iterator pattern

notion image
notion image

The iterator pattern as a language feature

Built-In Iterators
  • Java’s enhanced for statement
  • Python’s for/in statement
  • Javascript’s for/of statement
for el in [9, 8, 7, 6, 5]: print(el)
for (Animal a: animals) { a.describe(); a.makeSound(); }
for (let value of aggregate) { console.log(value); }

The Factory Patterns

Simple Factory Pattern

Pizza Store
notion image
Simple Factory Pattern
notion image

The Factory Method pattern

notion image
The Factory Method 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.
notion image
package src.patterns.factory_method; public class FactoryMethodApp { public static void main(String[] args) { PizzaStore pzStore; Pizza pizza; //Chicago style pzStore = new ChicagoStylePizzaStore(); pizza = pzStore.orderPizza("Cheese"); System.out.println("Delivery pizza " + pizza.getName()); //NY Style pzStore = new NYStylePizzaStore(); pizza = pzStore.orderPizza("Veggie"); System.out.println("Delivery pizza " + pizza.getName()); } } //ChicagoStylePizzaStore class package src.patterns.factory_method; public class ChicagoStylePizzaStore extends PizzaStore{ @Override public Pizza createPizza(String name) { Pizza pizza = null; switch(name){ case "Cheese": pizza = new ChicagoStyleCheesePizza(); break; case "Clam": pizza = new ChicagoStyleClamPizza(); break; case "Veggie": pizza = new ChicagoStyleVeggiePizza(); break; case "Pepperoni": pizza = new ChicagoStylePepperoniPizza(); break; default: break; } return pizza; } }

Challenge

notion image
  • Write code for Zone, its subclasses and the ZoneFactory class that will create the correct zone based on the zone id that you pass to its createZone() method

Solution

package src.patterns.simple_factory; public class ZoneFactory { public Zone createZone(String zoneId){ Zone zone; switch(zoneId){ case "Central": zone = new ZoneUSCentral(); break; case "Mountain": zone = new ZoneUSMountain(); break; case "Pacific": zone = new ZoneUSPacific(); break; case "Eastern": zone = new ZoneUSEastern(); break; default: zone = null; break; } return zone; } } //ZoneUSPacific class package src.patterns.simple_factory; public class ZoneUSPacific extends Zone { public ZoneUSPacific() { this.displayName = "Pacific US"; this.offset = -7; } } //Calendar package src.patterns.simple_factory; public abstract class Calendar { protected Zone zone; public void print() { System.out.println("Display name: " + zone.displayName + ", offset: " + zone.offset); } public abstract void createCalendar(); }

References