Learning Design Pattern Again

Essential Principles

  1. Encapsulate What Varies
  2. Program to an Interface, not an Implementation
  3. Favor Compositions, not Inheritance

SOLID Principles

Single Responsibility Principle

A class should have just one reason to change.

Open/Closed Principle

Classes should be open for extension but closed for modification.

Liskov Substitution Principle

When extending a class, remember that you should be able to pass objects of the subclass in place of objects of the parent class without breaking the client code.

Interface Segregation Principle

Clients shouldn’t be forced to depend methods they no not use.

Dependency Inversion Principle

High-level classes shouldn’t depend on low-level classes. Both should depend on abstractions. Abstractions shouldn’t depend on details. Details should depend on abstractions.

Creational Design Patterns

Mnemonics: AB-FPS

Factory Method

Error 400: Cannot load none (line: 2)

Main actors:

  • CarFactory
  • Car

Example:

1
2
3
4
5
6
func produceCar(by factory: CarFactory) -> Car {
return factory.produce()
}

let carA = produceCar(by: CarFactoryA())
let carB = produceCar(by: CarFactoryB())

Abstract Factory

Error 400: Cannot load none (line: 2)

Main actors:

  • CarFactory
  • Engine, Wheel, CarSheel

Example:

1
2
3
4
5
6
7
8
9
func produceCar(by factory: CarFactory) -> Car {
let carSheel = factory.makeCarShell()
let engine = factory.makeEngine()
let wheel = factory.makeWheel()
return assumble(carSheel, engine, wheel)
}

let carA = produceCar(by: CarFactoryA())
let carB = produceCar(by: CarFactoryB())

Builder

Error 400: Cannot load none (line: 2)

Example:

1
2
3
4
5
6
7
8
let builder = BuilderA()
builder.reset()
builder.buildStepA()
builder.buildStepB()
let productA = builder.getProduct()

let director = Director(builder: BuilderB())
let productB = director.make()

Prototype

Error 400: Cannot load none (line: 2)

Main actors:

  • Prototype

Example:

1
2
let originalConfiguration = ProtoType()
let copiedConfig = originalConfiguration.clone()

Singleton

Error 400: Cannot load none (line: 2)

Main actors:

  • Singleton

Example:

1
2
3
4
let a = Singleton.shared
let b = Singleton.shared
let c = Singleton() //FatalError
a == b // true

Structural Design Patterns

Mnemonics: ABC-PDF-F

Adapter

Objective Adapter:

Error 400: Cannot load none (line: 2)

Class Adapter:

Error 400: Cannot load none (line: 2)

Main actors:

  • DataAdapter

Example:

1
2
3
4
5
let thirdService = ThirdService()
let businessData = BusinessData()
let adapterData = AdapterData(innerData: businessData)

thirdService.handle(data: adapterData)

Bridge

The Abstract and Implementation aren’t the principle we understand about programing languages, like interface or abstract class.

Say we have a sharing card that has two schemes and two business logics. They can be combined to 4 kinds of compositions. If we use inheritance to implement it, we need to create 4 new classes.

We can divide these differences into two inheritances separately. Now we term the scheme layer as abstract, the business logic layer as implementation.

Error 400: Cannot load none (line: 2)

Composition

Error 400: Cannot load none (line: 2)

Proxy

Error 400: Cannot load none (line: 2)

Main actors:

  • Proxy

Example:

1
2
3
4
5
6
7
let client = Client()
let service = ConcreteService()

let proxy = Proxy(service)

client.inject(service: proxy)

Decorator

Error 400: Cannot load none (line: 2)

Main actors:

  • Component
  • ConcreteComponent
  • BaseDecorator, DecoratorA, DecoratorB

Facade

Error 400: Cannot load none (line: 2)

Main actors:

  • Facade

Flyweight

Error 400: Cannot load none (line: 2)

Main actors:

  • ImageStorage: FlyweightFactory
  • Image: Flyweight

Behavioral Design Patterns

Mnemonics: COMMITS-CSV

Chain of Responsibility

Error 400: Cannot load none (line: 2)

Main actors:

  • Handler

Examples:

1
2
3
4
5
6
7
8
9
10
11
let paymentRequest = Request()

let authenticationHandler = HandlerA()
let encryptHandler = HandlerB()
let payHandler = HandlerC()

authenticationHandler.setNext(encryptHandler)
encryptHandler.setNext(payHandler)

authenticationHandler.handle(request: paymentRequest)

Observer

Error 400: Cannot load none (line: 2)

Main actors:

  • Publisher
  • Subscriber

Examples:

1
2
3
4
5
6
7
8
9
10
let publisher = Publisher()
let subscriberA = ConcreteSubscriberA()
let subscriberB = ConcreteSubscriberB()

publisher.subscribe(subscriberA)
publisher.subscribe(subscriberB)

// The state in the publisher changed.
publisher.notifyAllSubscribers()

Mediator

Error 400: Cannot load none (line: 2)

Main actors:

  • Mediator
  • ConcreteMediator

Examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
protocol Mediator {
func notify(_ sender: Any)
}

class ConcreteMediator: Mediator {
var componentA: ComponentA?
var componentB: ComponentB?

func notify(_ sender: Any) {
if sender == A {
componentB.operationB()
}
}

func operationA() {
notify(self)
}
}

class ComponentA: Mediator {
private weak var mediator: Mediator?
func doOperation() {
mediator.notify(self)
}
}

class ComponentB: Mediator {
private weak var mediator: Mediator?
func doOperation() {
mediator.notify(self)
}

func operationB() {

}
}

let componentA = ComponentA()
let componentB = componentB()

let mediator = ConcreteMediator()
componentA.mediator = mediator
componentB.mediator = mediator

componentA.operation()
// componentB will invoke the operationB

Memento

Implementation based on an intermediate interface.

Error 400: Cannot load none (line: 2)

Implementation with even stricter encapsulation.

Error 400: Cannot load none (line: 2)

Iterator

Error 400: Cannot load none (line: 2)

Template Method

Error 400: Cannot load none (line: 2)

State

Error 400: Cannot load none (line: 2)

Main actors:

  • Context
  • State

Examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
protocol State {
func operation1()
func operation2()
}

class Context {
var state: State
func change(state: State) {
self.state = state
}
func action1() {
state.operation1()
state.operation2()
}

func action2 {
state.operation2()
}
}

class StateA: State {
weak var context: Context?

func operation1() {
// Do something
context.change(state: StateB())
}

func operation2() {

}
}

class StateB: State {
weak var context: Context?

func operation1() {
}

func operation2() {
// Do something
context.change(state: StateA())
}
}

Command

Error 400: Cannot load none (line: 2)

Strategy

Error 400: Cannot load none (line: 2)

Visitor

Error 400: Cannot load none (line: 2)

Swift can use protocol extension to replace the Visitor pattern.

Error 400: Cannot load none (line: 2)

Using the second plan can avoid modifications to File types. The Visitor patter needs to modify the declaration of the FileA and FileB to add a new method func visit(by visitor: Visitor).

Conclusion

Various Factory Principles

Answer

  • Factory
    It’s an ambiguous term that stands for a function, method or class that supposed to be producing something.

  • Creation method
    Every result of a factory method pattern is a creation method but not necessarily the reverse.
    A method that returns an instance object.

  • Static creation method
    It’s a creation method declared as static. You can invoke the method on a class without instantiating an object.
    Static creation methods are just convenience construction methods. We shouldn’t term it as Static factory method, because the factory method relies on inheritance. If you make it static, you can no longer extend it in its subclasses, which defeats the purpose of factory method pattern.
    A static method that returns an instance object, usually wraps construction methods.

  • Simple factory pattern
    The simple factory pattern describes a class that has one creation method with a large conditional that based on method parameters chooses which product class to instantiate and then return.
    When the creation method becomes more complex, you may want to extract these conditions to individual subclasses. Once you do it several times, you might find that the whole thing turned to the classic factory method pattern.
    A class has one method that returns an instance object of several kinds of classes based on conditions.

  • Factory method pattern
    A class(or interface) that defines an instance method returning an instance object(type of class or interface). You can create subclasses for the factory class to produce different instance objects by overriding the creation method.

  • Abstract factory pattern
    Like the factory method pattern, but has multiple creation methods that produce a group of related objects.