A short cheat-sheet with Xcode 8.2 Playground (Design-Patterns.playground.zip).
👷 Project maintained by: @nsmeme (Oktawian Chojnacki)
🚀 How to generate README, Playground and zip from source: GENERATE.md
Behavioral |
[Creational](Creational) |
[Structural](Structural)
In software engineering, behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication.
Source: wikipedia.org
import Swift
import Foundation
The chain of responsibility pattern is used to process varied requests, each of which may be dealt with by a different handler.
final class MoneyPile {
let value: Int
var quantity: Int
var nextPile: MoneyPile?
init(value: Int, quantity: Int, nextPile: MoneyPile?) {
self.value = value
self.quantity = quantity
self.nextPile = nextPile
}
func canWithdraw(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.nextPile {
return next.canWithdraw(amount: amount)
}
return false
}
}
final class ATM {
private var hundred: MoneyPile
private var fifty: MoneyPile
private var twenty: MoneyPile
private var ten: MoneyPile
private var startPile: MoneyPile {
return self.hundred
}
init(hundred: MoneyPile,
fifty: MoneyPile,
twenty: MoneyPile,
ten: MoneyPile) {
self.hundred = hundred
self.fifty = fifty
self.twenty = twenty
self.ten = ten
}
func canWithdraw(amount: Int) -> String {
return "Can withdraw: \(self.startPile.canWithdraw(amount: amount))"
}
}
// Create piles of money and link them together 10 < 20 < 50 < 100.**
let ten = MoneyPile(value: 10, quantity: 6, nextPile: nil)
let twenty = MoneyPile(value: 20, quantity: 2, nextPile: ten)
let fifty = MoneyPile(value: 50, quantity: 2, nextPile: twenty)
let hundred = MoneyPile(value: 100, quantity: 1, nextPile: fifty)
// Build ATM.
var atm = ATM(hundred: hundred, fifty: fifty, twenty: twenty, ten: ten)
atm.canWithdraw(amount: 310) // Cannot because ATM has only 300
atm.canWithdraw(amount: 100) // Can withdraw - 1x100
atm.canWithdraw(amount: 165) // Cannot withdraw because ATM doesn't has bill with value of 5
atm.canWithdraw(amount: 30) // Can withdraw - 1x20, 2x10
Further Examples: Design Patterns in Swift
The command pattern is used to express a request, including the call to be made and all of its required parameters, in a command object. The command may then be executed immediately or held for later use.
protocol DoorCommand {
func execute() -> String
}
class OpenCommand : DoorCommand {
let doors:String
required init(doors: String) {
self.doors = doors
}
func execute() -> String {
return "Opened \(doors)"
}
}
class CloseCommand : DoorCommand {
let doors:String
required init(doors: String) {
self.doors = doors
}
func execute() -> String {
return "Closed \(doors)"
}
}
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()
}
}
let podBayDoors = "Pod Bay Doors"
let doorModule = HAL9000DoorsOperations(doors:podBayDoors)
doorModule.open()
doorModule.close()
The interpreter pattern is used to evaluate sentences in a language.
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)
}
}
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)
Further Examples: Design Patterns in Swift
The iterator pattern is used to provide a standard interface for traversing a collection of items in an aggregate object without the need to understand its underlying structure.
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)
}
}
let greatNovellas = Novellas(novellas: [Novella(name: "The Mist")] )
for novella in greatNovellas {
print("I've read: \(novella)")
}
The mediator pattern is used to reduce coupling between classes that communicate with each other. Instead of classes communicating directly, and thus requiring knowledge of their implementation, the classes send messages via a mediator object.
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)
}
}
}
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)
Further Examples: Design Patterns in Swift
The memento pattern is used to capture the current state of an object and store it in such a manner that it can be restored at a later time without breaking the rules of encapsulation.
typealias Memento = NSDictionary
Originator
protocol MementoConvertible {
var memento: Memento { get }
init?(memento: Memento)
}
struct GameState: MementoConvertible {
private struct 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] as? String,
let mementoWeapon = memento[Keys.weapon] as? String else {
return nil
}
chapter = mementoChapter
weapon = mementoWeapon
}
var memento: Memento {
return [ Keys.chapter: chapter, Keys.weapon: weapon ]
}
}
Caretaker
enum CheckPoint {
static func save(_ state: MementoConvertible, saveName: String) {
let defaults = UserDefaults.standard
defaults.set(state.memento, forKey: saveName)
defaults.synchronize()
}
static func restore(saveName: String) -> Memento? {
let defaults = UserDefaults.standard
return defaults.object(forKey: saveName) as? Memento
}
}
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") {
let finalState = GameState(memento: memento)
dump(finalState)
}
The observer pattern is used to allow an object to publish changes to its state. Other objects subscribe to be immediately notified of any changes.
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.")
}
}
}
var observerInstance = Observer()
var testChambers = TestChambers()
testChambers.observer = observerInstance
testChambers.testChamberNumber += 1
Further Examples: Design Patterns in Swift
The state pattern is used to alter the behaviour of an object as its internal state changes. The pattern allows the class for an object to apparently change at run-time.
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 }
}
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)
Further Examples: Design Patterns in Swift
The strategy pattern is used to create an interchangeable family of algorithms from which the required process is chosen at run-time.
protocol PrintStrategy {
func print(_ string: String) -> String
}
final class Printer {
private let strategy: PrintStrategy
func print(_ string: String) -> String {
return self.strategy.print(string)
}
init(strategy: PrintStrategy) {
self.strategy = strategy
}
}
final class UpperCaseStrategy: PrintStrategy {
func print(_ string: String) -> String {
return string.uppercased()
}
}
final class LowerCaseStrategy: PrintStrategy {
func print(_ string:String) -> String {
return string.lowercased()
}
}
var lower = Printer(strategy: LowerCaseStrategy())
lower.print("O tempora, o mores!")
var upper = Printer(strategy: UpperCaseStrategy())
upper.print("O tempora, o mores!")
Further Examples: Design Patterns in Swift
The visitor pattern is used to separate a relatively complex set of structured data classes from the functionality that may be performed upon the data that they hold.
protocol PlanetVisitor {
func visit(planet: PlanetAlderaan)
func visit(planet: PlanetCoruscant)
func visit(planet: PlanetTatooine)
func visit(planet: MoonJedah)
}
protocol Planet {
func accept(visitor: PlanetVisitor)
}
class MoonJedah: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}
class PlanetAlderaan: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}
class PlanetCoruscant: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}
class PlanetTatooine: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}
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: MoonJedah) { name = "Jedah" }
}
let planets: [Planet] = [PlanetAlderaan(), PlanetCoruscant(), PlanetTatooine(), MoonJedah()]
let names = planets.map { (planet: Planet) -> String in
let visitor = NameVisitor()
planet.accept(visitor: visitor)
return visitor.name
}
names
Further Examples: Design Patterns in Swift
[Behavioral](Behavioral) |
Creational |
[Structural](Structural)
In software engineering, creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation.
Source: wikipedia.org
import Swift
import Foundation
The abstract factory pattern is used to provide a client with a set of related or dependant objects. The "family" of objects created by the factory are determined at run-time.
Protocols
protocol Decimal {
func stringValue() -> String
// factory
static func make(string : String) -> Decimal
}
typealias NumberFactory = (String) -> Decimal
// Number implementations with factory methods
struct NextStepNumber: Decimal {
private var nextStepNumber: NSNumber
func stringValue() -> String { return nextStepNumber.stringValue }
// factory
static func make(string: String) -> Decimal {
return NextStepNumber(nextStepNumber: NSNumber(value: (string as NSString).longLongValue))
}
}
struct SwiftNumber : Decimal {
private var swiftInt: Int
func stringValue() -> String { return "\(swiftInt)" }
// factory
static func make(string: String) -> Decimal {
return SwiftNumber(swiftInt:(string as NSString).integerValue)
}
}
Abstract factory
enum NumberType {
case nextStep, swift
}
enum NumberHelper {
static func factory(for type: NumberType) -> NumberFactory {
switch type {
case .nextStep:
return NextStepNumber.make
case .swift:
return SwiftNumber.make
}
}
}
let factoryOne = NumberHelper.factory(for: .nextStep)
let numberOne = factoryOne("1")
numberOne.stringValue()
let factoryTwo = NumberHelper.factory(for: .swift)
let numberTwo = factoryTwo("2")
numberTwo.stringValue()
The builder pattern is used to create complex objects with constituent parts that must be created in the same order or using a specific algorithm. An external class controls the construction algorithm.
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))"
}
}
let empire = DeathStarBuilder { builder in
builder.x = 0.1
builder.y = 0.2
builder.z = 0.3
}
let deathStar = DeathStar(builder:empire)
Further Examples: Design Patterns in Swift
The factory pattern is used to replace class constructors, abstracting the process of object generation so that the type of the object instantiated can be determined at run-time.
protocol Currency {
func symbol() -> String
func code() -> String
}
class Euro : Currency {
func symbol() -> String {
return "€"
}
func code() -> String {
return "EUR"
}
}
class UnitedStatesDolar : Currency {
func symbol() -> String {
return "$"
}
func code() -> String {
return "USD"
}
}
enum Country {
case unitedStates, spain, uk, greece
}
enum CurrencyFactory {
static func currency(for country:Country) -> Currency? {
switch country {
case .spain, .greece :
return Euro()
case .unitedStates :
return UnitedStatesDolar()
default:
return nil
}
}
}
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() ?? noCurrencyCode
The prototype pattern is used to instantiate a new object by copying all of the properties of an existing object, creating an independent clone. This practise is particularly useful when the construction of a new object is inefficient.
class ChungasRevengeDisplay {
var name: String?
let font: String
init(font: String) {
self.font = font
}
func clone() -> ChungasRevengeDisplay {
return ChungasRevengeDisplay(font:self.font)
}
}
let Prototype = ChungasRevengeDisplay(font:"GotanProject")
let Philippe = Prototype.clone()
Philippe.name = "Philippe"
let Christoph = Prototype.clone()
Christoph.name = "Christoph"
let Eduardo = Prototype.clone()
Eduardo.name = "Eduardo"
Further Examples: Design Patterns in Swift
The singleton pattern ensures that only one object of a particular class is ever created. All further references to objects of the singleton class refer to the same underlying instance. There are very few applications, do not overuse this pattern!
class DeathStarSuperlaser {
static let sharedInstance = DeathStarSuperlaser()
private init() {
// Private initialization to ensure just one instance is created.
}
}
let laser = DeathStarSuperlaser.sharedInstance
[Behavioral](Behavioral) |
[Creational](Creational) |
Structural
In software engineering, structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities.
Source: wikipedia.org
import Swift
import Foundation
The adapter pattern is used to provide a link between two otherwise incompatible types by wrapping the "adaptee" with a class that supports the interface required by the client.
protocol OlderDeathStarSuperLaserAiming {
var angleV: NSNumber {get}
var angleH: NSNumber {get}
}
Adaptee
struct DeathStarSuperlaserTarget {
let angleHorizontal: Double
let angleVertical: Double
init(angleHorizontal:Double, angleVertical:Double) {
self.angleHorizontal = angleHorizontal
self.angleVertical = angleVertical
}
}
Adapter
struct OldDeathStarSuperlaserTarget : OlderDeathStarSuperLaserAiming {
private let target : DeathStarSuperlaserTarget
var angleV:NSNumber {
return NSNumber(value: target.angleVertical)
}
var angleH:NSNumber {
return NSNumber(value: target.angleHorizontal)
}
init(_ target:DeathStarSuperlaserTarget) {
self.target = target
}
}
let target = DeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0)
let oldFormat = OldDeathStarSuperlaserTarget(target)
oldFormat.angleH
oldFormat.angleV
Further Examples: Design Patterns in Swift
The bridge pattern is used to separate the abstract elements of a class from the implementation details, providing the means to replace the implementation details without modifying the abstraction.
protocol Switch {
var appliance: Appliance {get set}
func turnOn()
}
protocol Appliance {
func run()
}
class RemoteControl: Switch {
var appliance: Appliance
func turnOn() {
self.appliance.run()
}
init(appliance: Appliance) {
self.appliance = appliance
}
}
class TV: Appliance {
func run() {
print("tv turned on");
}
}
class VacuumCleaner: Appliance {
func run() {
print("vacuum cleaner turned on")
}
}
var tvRemoteControl = RemoteControl(appliance: TV())
tvRemoteControl.turnOn()
var fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner())
fancyVacuumCleanerRemoteControl.turnOn()
The composite pattern is used to create hierarchical, recursive tree structures of related objects where any element of the structure may be accessed and utilised in a standard manner.
Component
protocol Shape {
func draw(fillColor: String)
}
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)")
}
}
Composite
final class Whiteboard : Shape {
lazy var shapes = [Shape]()
init(_ shapes:Shape...) {
self.shapes = shapes
}
func draw(fillColor: String) {
for shape in self.shapes {
shape.draw(fillColor: fillColor)
}
}
}
var whiteboard = Whiteboard(Circle(), Square())
whiteboard.draw("Red")
The decorator pattern is used to extend or alter the functionality of objects at run- time by wrapping them in an object of a decorator class. This provides a flexible alternative to using inheritance to modify behaviour.
protocol Coffee {
func getCost() -> Double
func getIngredients() -> String
}
class SimpleCoffee: Coffee {
func getCost() -> Double {
return 1.0
}
func getIngredients() -> String {
return "Coffee"
}
}
class CoffeeDecorator: Coffee {
private let decoratedCoffee: Coffee
fileprivate let ingredientSeparator: String = ", "
required init(decoratedCoffee: Coffee) {
self.decoratedCoffee = decoratedCoffee
}
func getCost() -> Double {
return decoratedCoffee.getCost()
}
func getIngredients() -> String {
return decoratedCoffee.getIngredients()
}
}
final class Milk: CoffeeDecorator {
required init(decoratedCoffee: Coffee) {
super.init(decoratedCoffee: decoratedCoffee)
}
override func getCost() -> Double {
return super.getCost() + 0.5
}
override func getIngredients() -> String {
return super.getIngredients() + ingredientSeparator + "Milk"
}
}
final class WhipCoffee: CoffeeDecorator {
required init(decoratedCoffee: Coffee) {
super.init(decoratedCoffee: decoratedCoffee)
}
override func getCost() -> Double {
return super.getCost() + 0.7
}
override func getIngredients() -> String {
return super.getIngredients() + ingredientSeparator + "Whip"
}
}
var someCoffee: Coffee = SimpleCoffee()
print("Cost : \(someCoffee.getCost()); Ingredients: \(someCoffee.getIngredients())")
someCoffee = Milk(decoratedCoffee: someCoffee)
print("Cost : \(someCoffee.getCost()); Ingredients: \(someCoffee.getIngredients())")
someCoffee = WhipCoffee(decoratedCoffee: someCoffee)
print("Cost : \(someCoffee.getCost()); Ingredients: \(someCoffee.getIngredients())")
The facade pattern is used to define a simplified interface to a more complex subsystem.
enum Eternal {
static func set(_ object: Any, forKey defaultName: String) {
let defaults: UserDefaults = UserDefaults.standard
defaults.set(object, forKey:defaultName)
defaults.synchronize()
}
static func object(forKey key: String) -> AnyObject! {
let defaults: UserDefaults = UserDefaults.standard
return defaults.object(forKey: key) as AnyObject!
}
}
Eternal.set("Disconnect me. I’d rather be nothing", forKey:"Bishop")
Eternal.object(forKey: "Bishop")
The flyweight pattern is used to minimize memory usage or computational expenses by sharing as much as possible with other similar objects.
// Instances of CoffeeFlavour will be the Flyweights
final class SpecialityCoffee: CustomStringConvertible {
var origin: String
var description: String {
get {
return origin
}
}
init(origin: String) {
self.origin = origin
}
}
// Menu acts as a factory and cache for CoffeeFlavour flyweight objects
final class Menu {
private var coffeeAvailable: [String: SpecialityCoffee] = [:]
func lookup(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 var menu = Menu()
func takeOrder(origin: String, table: Int) {
orders[table] = menu.lookup(origin: origin)
}
func serve() {
for (table, origin) in orders {
print("Serving \(origin) to table \(table)")
}
}
}
let coffeeShop = CoffeeShop()
coffeeShop.takeOrder(origin: "Yirgacheffe, Ethiopia", table: 1)
coffeeShop.takeOrder(origin: "Buziraguhindwa, Burundi", table: 3)
coffeeShop.serve()
The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. Protection proxy is restricting access.
protocol DoorOperator {
func open(doors: String) -> String
}
class HAL9000 : DoorOperator {
func open(doors: String) -> String {
return ("HAL9000: Affirmative, Dave. I read you. Opened \(doors).")
}
}
class CurrentComputer : DoorOperator {
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)
}
}
let computer = CurrentComputer()
let podBay = "Pod Bay Doors"
computer.open(doors: podBay)
computer.authenticate(password: "pass")
computer.open(doors: podBay)
The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. Virtual proxy is used for loading object on demand.
protocol HEVSuitMedicalAid {
func administerMorphine() -> String
}
class HEVSuit : HEVSuitMedicalAid {
func administerMorphine() -> String {
return "Morphine administered."
}
}
class HEVSuitHumanInterface : HEVSuitMedicalAid {
lazy private var physicalSuit: HEVSuit = HEVSuit()
func administerMorphine() -> String {
return physicalSuit.administerMorphine()
}
}
let humanInterface = HEVSuitHumanInterface()
humanInterface.administerMorphine()
📖 Descriptions from: Gang of Four Design Patterns Reference Sheet