Learning Design Pattern Again
Essential Principles
- Encapsulate What Varies
- Program to an Interface, not an Implementation
- 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
Main actors:
CarFactory
Car
Example:
1 | func produceCar(by factory: CarFactory) -> Car { |
Abstract Factory
Main actors:
CarFactory
Engine
,Wheel
,CarSheel
Example:
1 | func produceCar(by factory: CarFactory) -> Car { |
Builder
Example:
1 | let builder = BuilderA() |
Prototype
Main actors:
Prototype
Example:
1 | let originalConfiguration = ProtoType() |
Singleton
Main actors:
Singleton
Example:
1 | let a = Singleton.shared |
Structural Design Patterns
Mnemonics: ABC-PDF-F
Adapter
Objective Adapter:
Class Adapter:
Main actors:
DataAdapter
Example:
1 | let thirdService = ThirdService() |
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.
Composition
Proxy
Main actors:
Proxy
Example:
1 | let client = Client() |
Decorator
Main actors:
Component
ConcreteComponent
BaseDecorator
,DecoratorA
,DecoratorB
Facade
Main actors:
Facade
Flyweight
Main actors:
ImageStorage
: FlyweightFactoryImage
: Flyweight
Behavioral Design Patterns
Mnemonics: COMMITS-CSV
Chain of Responsibility
Main actors:
Handler
Examples:
1 | let paymentRequest = Request() |
Observer
Main actors:
Publisher
Subscriber
Examples:
1 | let publisher = Publisher() |
Mediator
Main actors:
Mediator
ConcreteMediator
Examples:
1 | protocol Mediator { |
Memento
Implementation based on an intermediate interface.
Implementation with even stricter encapsulation.
Iterator
Template Method
State
Main actors:
Context
State
Examples:
1 | protocol State { |
Command
Strategy
Visitor
Swift can use protocol extension to replace the Visitor pattern.
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
-
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 asstatic
. 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 asStatic factory method
, because the factory method relies on inheritance. If you make itstatic
, 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.