What is a Design Pattern?

• A standard solution to a common programming problem CS 326

Stephen Freund

Example 1: Encapsulation Example 2: Inheritance

• Problem: Exposed properties can be directly • Problem: Repetition in implementations manipulated – Similar abstractions have similar components – Violations of the representation invariant • Solution: Inherit default members from a – Dependences prevent changing the implementation superclass • Solution: Hide some components – Select an implementation via run-time dispatching – Constrain ways to access the object • Disadvantages: • Disadvantages: – Code for a class is spread out, and thus less – Interface may not (efficiently) provide all desired understandable operations to all clients – Hard to design and specify a superclass ahead of time – Indirection may reduce performance – Run-time dispatching introduces overhead

1 Example 3: Iteration Example 4: Generics

• Problem: To access all members of a collection, need a • Problem: specialized traversal for each data structure – Well-designed data structures only hold one type of – Introduces undesirable dependences object – Does not generalize to other collections • Solution: • Solution: – Programming language checks for errors in contents – The implementation provides traversal abstraction, does bookkeeping – Set instead of just Set – Results are communicated to clients via a standard interface • Disadvantages: (eg: Sequence methods) – More verbose types • Disadvantages: – Sometimes less understandable error messages – Iteration order fixed by the implementation and not under the control of the client

Other Examples Design Pattern in More Detail • Reuse implementation without subtyping • A standard solution to a common programming • Reuse implementation, but change interface problem • Permit a class to be instantiated only once – A design or implementation structure that achieves a • Constructor that might return an existing object particular purpose • Constructor that might return a subclass object – A high-level programming idiom • Combine behaviors without compile-time • A technique for making code more flexible extends clauses – Reduce coupling among program components • Shorthand for describing software design • You could come up with a solution to all of these – connections among components, heap structure, ... on your own, but why reinvent the wheel??? • Vocabulary for communication and documentation

2 When To Use A Design Pattern Canonical Reference

• Rule #1: Delay to avoid over-thinking – Get something basic and concrete working first – Improve or generalize it once you understand it • Design patterns can increase / decrease understandability – Improves modularity and flexibility, separates concerns, eases description – But usually adds indirection, increases code size • If you encounter a design problem, consider • aka: "Gang Of Four" Book design patterns that address that problem

Three Kinds Of Patterns Creational Patterns

• Creational patterns • Initializers are inflexible – constructing objects – Can't return a subtype of class they belong to • Structural patterns – Create new object, and never re-use existing one – combining objects, controlling heap layout • Behavioral patterns • Factory Patterns – communicating among objects, affecting object – ADT creators that are not Swift init()s semantics • Sharing Patterns: – Reuse objects to save space or share common state

3 Factories: Changing Implementations A Factory

• Supertypes support multiple implementations class MatrixFactory { public static func createMatrix() -> Matrix { – protocol Matrix { ... } ... – class SparseMatrix : Matrix { ... } return SparseMatrix() – class DenseMatrix : Matrix { ... } } • Clients use the supertype (Matrix) but still create } objects: • Clients call MatrixFactory.createMatrix() – let m : Matrix = SparseMatrix() or instead of a particular constructor – let m : Matrix = DenseMatrix() or ... +To switch implementation, change only one place • Switching implementations requires changing +createMatrix() can do arbitrary code computations to decide what kind of matrix to make

Example: Bicycle race Factory Method for Bicycles class Race { public init() { class Race { let bike1 = Bicycle() func createBicycle() -> Bicycle { let bike2 = Bicycle() Bicycle() … class TourDeFrance : Race { } class TourDeFrance : Race { } public init() { init() { func createBicycle() -> Bicycle { … let bike1 = RoadBicycle() let bike1 = RoadBicycle() } } let bike2 = RoadBicycle() createBicycle() … … class Cyclocross : Race { let bike2 = } } public init() { … createBicycle() let bike1 = MountainBicycle() class Cyclocross : Race { } … let bike2 = MountainBicycle() func createBicycle() -> Bicycle { … } MountainBicycle() … } } … … } } }

4 Factory Object for Bicycles Passing Factory Objects Around class BicycleFactory { func createBicycle() -> Bicycle { ... } class Race { func createWheel() -> Wheel { ... } init(factory : BicycleFactory) { func createFrame() -> Frame { ... } let bike1 = factory.createBicycle() } let bike2 = factory.createBicycle() … class RoadBicycleFactory: BicycleFactory { } override func createBicycle() -> Bicycle { } RoadBicycle() } class TourDeFrance : Race { } init() { super.init(factory: RoadBicycleFactory()) } } class MoutainBicycleFactory: BicycleFactory { class Cyclocross: Race { override func createBicycle() -> Bicycle { init() { super.init(factory: MountainBikFactory()) } MoutainBicycle() } } }

Separate Control of Bicycles / Races External Dependency Injection

class Race { • Java Example: init(factory : BicycleFactory) { – BicycleFactory f = new UnicycleFactory(); let bike1 = factory.createBicycle() – Race r = new TourDeFrance(f); let bike2 = factory.createBicycle() … • With external dependency injection: } – BicycleFactory f = ((BicycleFactory) } DependencyManager.get("BicycleFactory")); – Race r = new TourDeFrance(f); class TourDeFrance : Race { init(factory : BicycleFactory) { • Plus an external file: super.init(factory: factory) } init() { super.init(factory: RoadBicycleFactory()) } Tricycle } + Change the factory without recompiling let race = TourDeFrance(factory: unicycleFactory) - External file is essential part of program

5 External Dependency Injection Factories: Summary

• Interface Builder and Storyboards... • Problem: Want more flexible abstractions for what class to instantiate. • Factory method – Call method that can do any computation and return any subtype • Factory object – Bundles factory methods for a family of types – Can store factory object, pass to constructors, etc. • Dependency Injection – Put choice of subclass in a file to avoid source-code changes or even recompiling when decision changes

Design Patterns for Sharing Singleton

• Problem: Swift initializers always return a new • Only one object of the given type exists object, never a pre-existing object • Good for unique, shared resources – UserDefaults.standard – DispatchQueue.main • Singleton: only one object exists at runtime – UIApplication.sharedApplication() – Factory method returns the same object every time – Logger for diagnostic messages • Better than lots of global properties • Interning: only one object with a particular – logically group related values (abstract) value exists at run time – Can use initializer / factory to customize – Factory method returns an existing object, not a new – eg: Internationalization: messages in a particular one language

6 Creating Singletons Interning pattern

• In Swift class: • Reuse existing objects instead of creating new ones

– public constant property to hold singleton object "Stephen Freund" name – private initializer street: "47 Lab Campus Drive" address town: "Williamstown, MA 01267"

class Logger { ... static public let instance = Logger() "Bill Lenhart" private init() { ... } name street: "47 Lab Campus Drive" } address town: "Williamstown, MA 01267"

• In client: class Address : Hashable { let street : String – Refer to the single instance of the Singleton class let town : String – Logger.instance.print("button clicked") ... }

Interning pattern Simple Interning Mechanism

• Reuse existing objects instead of creating new ones • Maintain a collection of all objects "Stephen Freund" • name If an object already appears, return that instead street: "47 Lab Campus Drive" var interned = Set

() address town: "Williamstown, MA 01267"

func intern(_ n : Address) -> Address { ... // inserts if not present, returns elem == n in set. "Bill Lenhart" let (_, memberAfterInsert) = interned.insert(n) name return memberAfterInsert address } • Less space • objects can be compared with === instead of == • Create the object, but perhaps discard it and • Sensible only for immutable objects return another when interning.

7 java.lang.Boolean and Interning public class Boolean { private final boolean value; Should have never been // construct a new Boolean value public Boolean(boolean value) { made public this.value = value; }

// Singletons for true and false public static Boolean FALSE = new Boolean(false); public static Boolean TRUE = new Boolean(true);

// factory method that uses interning public static Boolean valueOf(boolean value) { return value ? TRUE : FALSE; } Boolean b = Boolean.valueOf(true); } vs Boolean b = new Boolean(true);

8