👷 Source items are maintained by @nsmeme (Oktawian Chojnacki).
🇨🇳 Chinese version edited and translated by @binglogo.
print("Welcome!" )Copy the code
directory
Behavioral pattern | Creation pattern | Structural mode |
---|---|---|
🐝 Chain Of Responsibility | 🌰 Abstract Factory | 🔌 Adapter |
👫 Command Command | 👷 Builder | 🌉 Bridge Bridge |
🎶 Interpreter | 🏭 Factory Method Factory Method | The Composite 🌿 combination |
🍫 Iterator Iterator | 🃏 Prototype Prototype | 🍧 modified Decorator |
💐 Mediator | 💍 Singleton Singleton | 🎁 exterior Facade |
💾 Memento | 🍃 Flyweight Flyweight | |
👓 Observer | ☔ Protection Proxy | |
🐉 State State | 🍬 Virtual Proxy Virtual Proxy | |
💡 Strategy Strategy | ||
🏃 Visitor Visitor |
Behavioral pattern
In software engineering, behavioral patterns are a type of design patterns used to identify common communication patterns between objects and implement them. This allows for increased flexibility in these communications.
Source: Wikipedia
🐝 Chain Of Responsibility
The chain of responsibility pattern is a software design pattern in object-oriented programming, which contains some command objects and a series of processing objects. Each processing object determines which command objects it can process, and it knows how to pass command objects that it cannot process to the next processing object in the chain.
Example:
protocol Withdrawing {
func withdraw(amount: Int) -> Bool
}
final class MoneyPile: Withdrawing {
let value: Int
var quantity: Int
var next: Withdrawing?
init(value: Int, quantity: Int, next: Withdrawing?) {
self.value = value
self.quantity = quantity
self.next = next
}
func withdraw(amount: Int) -> Bool {
var amount = amount
func canTakeSomeBill(want: Int) -> Bool {
return (want / self.value) > 0
}
var quantity = self.quantity
while canTakeSomeBill(want: amount) {
if quantity == 0 {
break
}
amount -= self.value
quantity -= 1
}
guard amount > 0 else {
return true
}
if let next = self.next {
return next.withdraw(amount: amount)
}
return false
}
}
final class ATM: Withdrawing {
private var hundred: Withdrawing
private var fifty: Withdrawing
private var twenty: Withdrawing
private var ten: Withdrawing
private var startPile: Withdrawing {
return self.hundred
}
init(hundred: Withdrawing,
fifty: Withdrawing,
twenty: Withdrawing,
ten: Withdrawing) {
self.hundred = hundred
self.fifty = fifty
self.twenty = twenty
self.ten = ten
}
func withdraw(amount: Int) -> Bool {
return startPile.withdraw(amount: amount)
}
}Copy the code
usage
Let ten = MoneyPile(value: 10, quantity: 6, next: nil) let twenty = MoneyPile(value: 10, quantity: 6, next: nil) 20, quantity: 2, next: ten) let fifty = MoneyPile(value: 50, quantity: 2, next: twenty) let hundred = MoneyPile(value: Var ATM = ATM(hundred: hundred, fifty: fifty, twenty: twenty, ten: ten) atm.withdraw(amount: 310) // Cannot because ATM has only 300 atm.withdraw(amount: 100) // Can withdraw - 1x100Copy the code
👫 Command
Command pattern is a design pattern that attempts to represent actual actions in terms of objects. Command objects can encapsulate actions and their parameters, so that these actions can be:
- Repeated many times
- Cancel (if the object has an implementation)
- Cancel and do it again
Example:
protocol DoorCommand {
func execute() -> String
}
final class OpenCommand: DoorCommand {
let doors:String
required init(doors: String) {
self.doors = doors
}
func execute() -> String {
return "Opened \(doors)"
}
}
final class CloseCommand: DoorCommand {
let doors:String
required init(doors: String) {
self.doors = doors
}
func execute() -> String {
return "Closed \(doors)"
}
}
final class HAL9000DoorsOperations {
let openCommand: DoorCommand
let closeCommand: DoorCommand
init(doors: String) {
self.openCommand = OpenCommand(doors:doors)
self.closeCommand = CloseCommand(doors:doors)
}
func close() -> String {
return closeCommand.execute()
}
func open() -> String {
return openCommand.execute()
}
}Copy the code
usage
let podBayDoors = "Pod Bay Doors"
let doorModule = HAL9000DoorsOperations(doors:podBayDoors)
doorModule.open()
doorModule.close()Copy the code
🎶 Interpreter
Given a language, define a representation of its grammar and define an interpreter that uses that representation to interpret sentences in the language.
Example:
protocol IntegerExpression {
func evaluate(_ context: IntegerContext) -> Int
func replace(character: Character, integerExpression: IntegerExpression) -> IntegerExpression
func copied() -> IntegerExpression
}
final class IntegerContext {
private var data: [Character:Int] = [:]
func lookup(name: Character) -> Int {
return self.data[name]!
}
func assign(expression: IntegerVariableExpression, value: Int) {
self.data[expression.name] = value
}
}
final class IntegerVariableExpression: IntegerExpression {
let name: Character
init(name: Character) {
self.name = name
}
func evaluate(_ context: IntegerContext) -> Int {
return context.lookup(name: self.name)
}
func replace(character name: Character, integerExpression: IntegerExpression) -> IntegerExpression {
if name == self.name {
return integerExpression.copied()
} else {
return IntegerVariableExpression(name: self.name)
}
}
func copied() -> IntegerExpression {
return IntegerVariableExpression(name: self.name)
}
}
final class AddExpression: IntegerExpression {
private var operand1: IntegerExpression
private var operand2: IntegerExpression
init(op1: IntegerExpression, op2: IntegerExpression) {
self.operand1 = op1
self.operand2 = op2
}
func evaluate(_ context: IntegerContext) -> Int {
return self.operand1.evaluate(context) + self.operand2.evaluate(context)
}
func replace(character: Character, integerExpression: IntegerExpression) -> IntegerExpression {
return AddExpression(op1: operand1.replace(character: character, integerExpression: integerExpression),
op2: operand2.replace(character: character, integerExpression: integerExpression))
}
func copied() -> IntegerExpression {
return AddExpression(op1: self.operand1, op2: self.operand2)
}
}Copy the code
usage
var context = IntegerContext()
var a = IntegerVariableExpression(name: "A")
var b = IntegerVariableExpression(name: "B")
var c = IntegerVariableExpression(name: "C")
var expression = AddExpression(op1: a, op2: AddExpression(op1: b, op2: c)) // a + (b + c)
context.assign(expression: a, value: 2)
context.assign(expression: b, value: 1)
context.assign(expression: c, value: 3)
var result = expression.evaluate(context)Copy the code
🍫 Iterator
The iterator pattern allows users to tour each element in a container through a specific interface without knowing the underlying implementation.
Example:
struct Novella {
let name: String
}
struct Novellas {
let novellas: [Novella]
}
struct NovellasIterator: IteratorProtocol {
private var current = 0
private let novellas: [Novella]
init(novellas: [Novella]) {
self.novellas = novellas
}
mutating func next() -> Novella? {
defer { current += 1 }
return novellas.count > current ? novellas[current] : nil
}
}
extension Novellas: Sequence {
func makeIterator() -> NovellasIterator {
return NovellasIterator(novellas: novellas)
}
}Copy the code
usage
let greatNovellas = Novellas(novellas: [Novella(name: "The Mist")] )
for novella in greatNovellas {
print("I've read: \(novella)")
}Copy the code
💐 Mediators
Encapsulating a set of object interactions with a mediator object, the mediator loosens the coupling by allowing the objects to interact without explicitly, and can change their interactions independently.
Example:
protocol Receiver {
associatedtype MessageType
func receive(message: MessageType)
}
protocol Sender {
associatedtype MessageType
associatedtype ReceiverType: Receiver
var recipients: [ReceiverType] { get }
func send(message: MessageType)
}
struct Programmer: Receiver {
let name: String
init(name: String) {
self.name = name
}
func receive(message: String) {
print("\(name) received: \(message)")
}
}
final class MessageMediator: Sender {
internal var recipients: [Programmer] = []
func add(recipient: Programmer) {
recipients.append(recipient)
}
func send(message: String) {
for recipient in recipients {
recipient.receive(message: message)
}
}
}
Copy the code
usage
func spamMonster(message: String, worker: MessageMediator) {
worker.send(message: message)
}
let messagesMediator = MessageMediator()
let user0 = Programmer(name: "Linus Torvalds")
let user1 = Programmer(name: "Avadis 'Avie' Tevanian")
messagesMediator.add(recipient: user0)
messagesMediator.add(recipient: user1)
spamMonster(message: "I'd Like to Add you to My Professional Network", worker: messagesMediator)
Copy the code
💾 Memento
Capture the internal state of an object and store the state outside of the object without breaking encapsulation. This will restore the object to its original saved state
Example:
typealias Memento = [String: String]Copy the code
Originator
protocol MementoConvertible { var memento: Memento { get } init? (memento: Memento) } struct GameState: MementoConvertible { private enum Keys { static let chapter = "com.valve.halflife.chapter" static let weapon = "com.valve.halflife.weapon" } var chapter: String var weapon: String init(chapter: String, weapon: String) { self.chapter = chapter self.weapon = weapon } init? (memento: Memento) { guard let mementoChapter = memento[Keys.chapter], let mementoWeapon = memento[Keys.weapon] else { return nil } chapter = mementoChapter weapon = mementoWeapon } var memento: Memento { return [ Keys.chapter: chapter, Keys.weapon: weapon ] } }Copy the code
Caretaker (Caretaker)
enum CheckPoint {
private static let defaults = UserDefaults.standard
static func save(_ state: MementoConvertible, saveName: String) {
defaults.set(state.memento, forKey: saveName)
defaults.synchronize()
}
static func restore(saveName: String) -> Any? {
return defaults.object(forKey: saveName)
}
}Copy the code
usage
var gameState = GameState(chapter: "Black Mesa Inbound", weapon: "Crowbar")
gameState.chapter = "Anomalous Materials"
gameState.weapon = "Glock 17"
CheckPoint.save(gameState, saveName: "gameState1")
gameState.chapter = "Unforeseen Consequences"
gameState.weapon = "MP5"
CheckPoint.save(gameState, saveName: "gameState2")
gameState.chapter = "Office Complex"
gameState.weapon = "Crossbow"
CheckPoint.save(gameState, saveName: "gameState3")
if let memento = CheckPoint.restore(saveName: "gameState1") as? Memento {
let finalState = GameState(memento: memento)
dump(finalState)
}Copy the code
👓 Observer
A target object manages all observer objects that depend on it and actively notifies itself when its state changes
Example:
protocol PropertyObserver : class { func willChange(propertyName: String, newPropertyValue: Any?) func didChange(propertyName: String, oldPropertyValue: Any?) } final class TestChambers { weak var observer:PropertyObserver? private let testChamberNumberName = "testChamberNumber" var testChamberNumber: Int = 0 { willSet(newValue) { observer? .willChange(propertyName: testChamberNumberName, newPropertyValue: newValue) } didSet { observer? .didChange(propertyName: testChamberNumberName, oldPropertyValue: oldValue) } } } final class Observer : PropertyObserver { func willChange(propertyName: String, newPropertyValue: Any?) { if newPropertyValue as? Int == 1 { print("Okay. Look. We both said a lot of things that you're going to regret.") } } func didChange(propertyName: String, oldPropertyValue: Any?) { if oldPropertyValue as? Int == 0 { print("Sorry about the mess. I've really let the place go since you killed me.") } } }Copy the code
usage
var observerInstance = Observer()
var testChambers = TestChambers()
testChambers.observer = observerInstance
testChambers.testChamberNumber += 1Copy the code
🐉 Status (State)
In state mode, the behavior of an object changes based on its internal state. This pattern allows a class object to change at run time.
Example:
final class Context { private var state: State = UnauthorizedState() var isAuthorized: Bool { get { return state.isAuthorized(context: self) } } var userId: String? { get { return state.userId(context: self) } } func changeStateToAuthorized(userId: String) { state = AuthorizedState(userId: userId) } func changeStateToUnauthorized() { state = UnauthorizedState() } } protocol State { func isAuthorized(context: Context) -> Bool func userId(context: Context) -> String? } class UnauthorizedState: State { func isAuthorized(context: Context) -> Bool { return false } func userId(context: Context) -> String? { return nil } } class AuthorizedState: State { let userId: String init(userId: String) { self.userId = userId } func isAuthorized(context: Context) -> Bool { return true } func userId(context: Context) -> String? { return userId } }Copy the code
usage
let userContext = Context()
(userContext.isAuthorized, userContext.userId)
userContext.changeStateToAuthorized(userId: "admin")
(userContext.isAuthorized, userContext.userId) // now logged in as "admin"
userContext.changeStateToUnauthorized()
(userContext.isAuthorized, userContext.userId)Copy the code
💡 Strategy
Objects have a behavior, but different scenarios have different algorithms for implementing that behavior. Strategy mode:
- Defines a family of algorithms (business rules);
- Encapsulate each algorithm;
- Algorithms of this family are interchangeable.
Example:
struct TestSubject { let pupilDiameter: Double let blushResponse: Double let isOrganic: Bool } protocol RealnessTesting: AnyObject { func testRealness(_ testSubject: TestSubject) -> Bool } final class VoightKampffTest: RealnessTesting { func testRealness(_ testSubject: TestSubject) - > Bool {return TestSubject. PupilDiameter < 30.0 | | TestSubject. BlushResponse = = 0.0}} final class GeneticTest: RealnessTesting { func testRealness(_ testSubject: TestSubject) -> Bool { return testSubject.isOrganic } } final class BladeRunner { private let strategy: RealnessTesting init(test: RealnessTesting) { self.strategy = test } func testIfAndroid(_ testSubject: TestSubject) -> Bool { return ! strategy.testRealness(testSubject) } }Copy the code
usage
Let Rachel = TestSubject(Flushing diameter: 30.2, blushResponse: 0.3, isOrganic: false) // Deckard is using a traditional test let deckard = BladeRunner(test: VoightKampffTest()) let isRachelAndroid = deckard.testIfAndroid(rachel) // Gaff is using a very precise method let gaff = BladeRunner(test: GeneticTest()) let isDeckardAndroid = gaff.testIfAndroid(rachel)Copy the code
🏃 Visitor
Encapsulates operations that operate on elements of a data structure and defines new operations that operate on those elements without changing the data structure.
Example:
protocol PlanetVisitor {
func visit(planet: PlanetAlderaan)
func visit(planet: PlanetCoruscant)
func visit(planet: PlanetTatooine)
func visit(planet: MoonJedha)
}
protocol Planet {
func accept(visitor: PlanetVisitor)
}
final class MoonJedha: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}
final class PlanetAlderaan: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}
final class PlanetCoruscant: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}
final class PlanetTatooine: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}
final class NameVisitor: PlanetVisitor {
var name = ""
func visit(planet: PlanetAlderaan) { name = "Alderaan" }
func visit(planet: PlanetCoruscant) { name = "Coruscant" }
func visit(planet: PlanetTatooine) { name = "Tatooine" }
func visit(planet: MoonJedha) { name = "Jedha" }
}
Copy the code
usage
let planets: [Planet] = [PlanetAlderaan(), PlanetCoruscant(), PlanetTatooine(), MoonJedha()]
let names = planets.map { (planet: Planet) -> String in
let visitor = NameVisitor()
planet.accept(visitor: visitor)
return visitor.name
}
namesCopy the code
Creation pattern
The creation pattern is a design pattern that deals with object creation, trying to create objects in a way that suits the situation. Basic object creation can cause design problems or add complexity to your design. The creative pattern solves the problem by controlling the creation of objects in some way.
Source: Wikipedia
🌰 Abstract Factory
The abstract factory pattern provides a way to encapsulate a set of separate factories with the same topic. In normal use, a client program creates a concrete implementation of an abstract factory, and then uses the abstract factory as an interface to create concrete objects for that topic.
Example:
agreement
protocol BurgerDescribing { var ingredients: [String] { get } } struct CheeseBurger: BurgerDescribing { let ingredients: [String]} protocol making {func make() -> render render} BurgerMaking { func make() -> BurgerDescribing { return CheeseBurger(ingredients: ["Cheese", "Burger", "Lettuce", "Tomato"]) } } final class JackInTheBox: BurgerMaking { func make() -> BurgerDescribing { return CheeseBurger(ingredients: ["Cheese", "Burger", "Tomato", "Onions"]) } }Copy the code
The abstract factory
enum BurgerFactoryType: BurgerMaking {
case bigKahuna
case jackInTheBox
func make() -> BurgerDescribing {
switch self {
case .bigKahuna:
return BigKahunaBurger().make()
case .jackInTheBox:
return JackInTheBox().make()
}
}
}Copy the code
usage
let bigKahuna = BurgerFactoryType.bigKahuna.make()
let jackInTheBox = BurgerFactoryType.jackInTheBox.make()Copy the code
👷 Builder
An object construction pattern. It can abstract the construction process of complex objects (abstract classes), so that different implementation methods of this abstract process can construct objects with different manifestations (attributes).
Example:
final class DeathStarBuilder { var x: Double? var y: Double? var z: Double? typealias BuilderClosure = (DeathStarBuilder) -> () init(buildClosure: BuilderClosure) { buildClosure(self) } } struct DeathStar : CustomStringConvertible { let x: Double let y: Double let z: Double init? (builder: DeathStarBuilder) { if let x = builder.x, let y = builder.y, let z = builder.z { self.x = x self.y = y self.z = z } else { return nil } } var description:String { return "Death Star at (x:\(x) y:\(y) z:\(z))" } }Copy the code
usage
Let empire = DeathStarBuilder {builder in builder. X = 0.1 builder. Y = 0.2 builder. Z = 0.3} let deathStar = DeathStar(builder:empire)Copy the code
🏭 Factory Method
Define an interface that creates an object, but let the class that implements the interface decide which class to instantiate. The factory method lets class instantiation be deferred to subclasses.
Example:
protocol CurrencyDescribing { var symbol: String { get } var code: String { get } } final class Euro: CurrencyDescribing {var symbol: String {return "€"} var code: String { return "EUR" } } final class UnitedStatesDolar: CurrencyDescribing { var symbol: String { return "$" } var code: String { return "USD" } } enum Country { case unitedStates case spain case uk case greece } enum CurrencyFactory { static func currency(for country: Country) -> CurrencyDescribing? { switch country { case .spain, .greece: return Euro() case .unitedStates: return UnitedStatesDolar() default: return nil } } }Copy the code
usage
let noCurrencyCode = "No Currency Code Available" CurrencyFactory.currency(for: .greece)? .code ?? noCurrencyCode CurrencyFactory.currency(for: .spain)? .code ?? noCurrencyCode CurrencyFactory.currency(for: .unitedStates)? .code ?? noCurrencyCode CurrencyFactory.currency(for: .uk)? .code ?? noCurrencyCodeCopy the code
🃏 Prototype
Return a new instance by “copying” an existing one, rather than creating a new one. The copied instance is what we call a “prototype,” which is customizable.
Example:
struct MoonWorker {
let name: String
var health: Int = 100
init(name: String) {
self.name = name
}
func clone() -> MoonWorker {
return MoonWorker(name: name)
}
}Copy the code
usage
let prototype = MoonWorker(name: "Sam Bell")
var bell1 = prototype.clone()
bell1.health = 12
var bell2 = prototype.clone()
bell2.health = 23
var bell3 = prototype.clone()
bell3.health = 0Copy the code
💍 Singleton
A singleton class must ensure that only one instance exists. Many times the whole system only needs to have one global object, which helps us coordinate the behavior of the whole system
Example:
final class ElonMusk {
static let shared = ElonMusk()
private init() {
// Private initialization to ensure just one instance is created.
}
}Copy the code
usage
let elon = ElonMusk.shared // There is only one Elon Musk folks.Copy the code
Structural mode
In software engineering, structural patterns are design patterns that simplify design by understanding the relationships between components in a consistent way.
Source: Wikipedia
🔌 Adapter
Adapter patterns are sometimes called wrapper styles or wrappers. Redirect the interface of a class to what the user expects. An adaptation enables classes that cannot work together because of incompatible interfaces to work together by wrapping the class’s own interface in an existing class.
Example:
protocol NewDeathStarSuperLaserAiming {
var angleV: Double { get }
var angleH: Double { get }
}Copy the code
By adaptation
struct OldDeathStarSuperlaserTarget {
let angleHorizontal: Float
let angleVertical: Float
init(angleHorizontal: Float, angleVertical: Float) {
self.angleHorizontal = angleHorizontal
self.angleVertical = angleVertical
}
}Copy the code
The adapter
struct NewDeathStarSuperlaserTarget: NewDeathStarSuperLaserAiming {
private let target: OldDeathStarSuperlaserTarget
var angleV: Double {
return Double(target.angleVertical)
}
var angleH: Double {
return Double(target.angleHorizontal)
}
init(_ target: OldDeathStarSuperlaserTarget) {
self.target = target
}
}Copy the code
usage
Let target = OldDeathStarSuperlaserTarget (angleHorizontal: 14.0, angleVertical: 12.0) the let newFormat = NewDeathStarSuperlaserTarget (target) newFormat. AngleH newFormat. AngleVCopy the code
🌉 Bridge
The bridge pattern separates the abstract parts from the implementation parts so that they can vary independently.
Example:
protocol Switch {
var appliance: Appliance { get set }
func turnOn()
}
protocol Appliance {
func run()
}
final class RemoteControl: Switch {
var appliance: Appliance
func turnOn() {
self.appliance.run()
}
init(appliance: Appliance) {
self.appliance = appliance
}
}
final class TV: Appliance {
func run() {
print("tv turned on");
}
}
final class VacuumCleaner: Appliance {
func run() {
print("vacuum cleaner turned on")
}
}Copy the code
usage
let tvRemoteControl = RemoteControl(appliance: TV())
tvRemoteControl.turnOn()
let fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner())
fancyVacuumCleanerRemoteControl.turnOn()Copy the code
🌿 Composite
Group objects into a tree structure to represent a partial-whole hierarchy. The composite pattern makes the use of single objects and composite objects consistent.
Example:
Component (Component)
protocol Shape {
func draw(fillColor: String)
}Copy the code
Leaf nodes (Leafs)
final class Square: Shape {
func draw(fillColor: String) {
print("Drawing a Square with color \(fillColor)")
}
}
final class Circle: Shape {
func draw(fillColor: String) {
print("Drawing a circle with color \(fillColor)")
}
}
Copy the code
combination
final class Whiteboard: Shape {
private lazy var shapes = [Shape]()
init(_ shapes: Shape...) {
self.shapes = shapes
}
func draw(fillColor: String) {
for shape in self.shapes {
shape.draw(fillColor: fillColor)
}
}
}Copy the code
usage
var whiteboard = Whiteboard(Circle(), Square())
whiteboard.draw(fillColor: "Red")Copy the code
🍧 Decorator
Decoration pattern is a design pattern that dynamically adds new behavior to a class in the field of object-oriented programming. In terms of functionality, decorated patterns are more flexible than subclassing, so you can add functionality to an object rather than the entire class.
Example:
protocol CostHaving { var cost: Double { get } } protocol IngredientsHaving { var ingredients: [String] { get } } typealias BeverageDataHaving = CostHaving & IngredientsHaving struct SimpleCoffee: BeverageDataHaving {let cost: Double = 1.0 let ingredients = ["Water", "Coffee"]} protocol BeverageHaving: BeverageDataHaving { var beverage: BeverageDataHaving { get } } struct Milk: BeverageHaving { let beverage: BeverageDataHaving var cost: Double {return beverage.cost + 0.5} var 2. [String] { return beverage.ingredients + ["Milk"] } } struct WhipCoffee: BeverageHaving { let beverage: BeverageDataHaving var cost: Double {return beverage.cost + 0.5} var 2. [String] { return beverage.ingredients + ["Whip"] } }Copy the code
usage
var someCoffee: BeverageDataHaving = SimpleCoffee()
print("Cost: \(someCoffee.cost); Ingredients: \(someCoffee.ingredients)")
someCoffee = Milk(beverage: someCoffee)
print("Cost: \(someCoffee.cost); Ingredients: \(someCoffee.ingredients)")
someCoffee = WhipCoffee(beverage: someCoffee)
print("Cost: \(someCoffee.cost); Ingredients: \(someCoffee.ingredients)")Copy the code
🎁 Facade
The facade pattern provides a unified high-level interface for a set of interfaces in a subsystem, making the subsystem easier to use.
Example:
final class Defaults {
private let defaults: UserDefaults
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
}
subscript(key: String) -> String? {
get {
return defaults.string(forKey: key)
}
set {
defaults.set(newValue, forKey: key)
}
}
}Copy the code
usage
Let storage = Defaults() // Store storage["Bishop"] = "Disconnect me. I'd rather be nothing"Copy the code
🍃 Flyweight
Use shared objects to minimize memory usage and share information with as many similar objects as possible; It is suitable for use when a large number of objects are simply repeated and thus unacceptably use large amounts of memory.
Example:
Struct SpecialityCoffee {let origin: String} protocol CoffeeSearching {func search(origin: String) -> SpecialityCoffee? } // Final class Menu: CoffeeSearching {private var coffeeAvailable: [String: SpecialityCoffee] = [:] func search(origin: String) -> SpecialityCoffee? { if coffeeAvailable.index(forKey: origin) == nil { coffeeAvailable[origin] = SpecialityCoffee(origin: origin) } return coffeeAvailable[origin] } } final class CoffeeShop { private var orders: [Int: SpecialityCoffee] = [:] private let menu: CoffeeSearching init(menu: CoffeeSearching) { self.menu = menu } func takeOrder(origin: String, table: Int) { orders[table] = menu.search(origin: origin) } func serve() { for (table, origin) in orders { print("Serving \(origin) to table \(table)") } } }Copy the code
usage
let coffeeShop = CoffeeShop(menu: Menu())
coffeeShop.takeOrder(origin: "Yirgacheffe, Ethiopia", table: 1)
coffeeShop.takeOrder(origin: "Buziraguhindwa, Burundi", table: 3)
coffeeShop.serve()Copy the code
☔ Protection Proxy
In proxy mode, a class is created to represent the functionality of another underlying class. A guard agent is used to restrict access.
Example:
protocol DoorOpening { func open(doors: String) -> String } final class HAL9000: DoorOpening { func open(doors: String) -> String { return ("HAL9000: Affirmative, Dave. I read you. Opened \(doors).") } } final class CurrentComputer: DoorOpening { private var computer: HAL9000! func authenticate(password: String) -> Bool { guard password == "pass" else { return false } computer = HAL9000() return true } func open(doors: String) -> String { guard computer ! = nil else { return "Access Denied. I'm afraid I can't do that." } return computer.open(doors: doors) } }Copy the code
usage
let computer = CurrentComputer()
let podBay = "Pod Bay Doors"
computer.open(doors: podBay)
computer.authenticate(password: "pass")
computer.open(doors: podBay)Copy the code
🍬 Virtual Proxy
In proxy mode, a class is created to represent the functionality of another underlying class. Virtual proxies are used for on-demand loading of objects.
Example:
protocol HEVSuitMedicalAid {
func administerMorphine() -> String
}
final class HEVSuit: HEVSuitMedicalAid {
func administerMorphine() -> String {
return "Morphine administered."
}
}
final class HEVSuitHumanInterface: HEVSuitMedicalAid {
lazy private var physicalSuit: HEVSuit = HEVSuit()
func administerMorphine() -> String {
return physicalSuit.administerMorphine()
}
}Copy the code
usage
let humanInterface = HEVSuitHumanInterface()
humanInterface.administerMorphine()Copy the code
Info
📖 Descriptions from: Gang of Four Design Patterns Reference Sheet