diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..dbf5acd --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: ochococo diff --git a/.github/workflows/generate-playground.yml b/.github/workflows/generate-playground.yml new file mode 100644 index 0000000..89f3980 --- /dev/null +++ b/.github/workflows/generate-playground.yml @@ -0,0 +1,75 @@ +name: Generate Playground Files and READMES + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: [master] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + generate-playgrounds: + # The type of runner that the job will run on + runs-on: macos-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + # Checkout & Generate + - name: Generate playgrounds and readmes + run: | + ./generate-playground.sh + ./generate-playground-cn.sh + + # Commit + - name: Commit files + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add Design-Patterns-CN.playground.zip + git add Design-Patterns.playground.zip + git add README.md + git add README-CN.md + git commit -m "Generate Playground" + ## Push changes to master branch + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: "master" + force: false + + # This workflow contains a single job called "build" + generate-chinese-branch: + needs: generate-playgrounds + # The type of runner that the job will run on + runs-on: macos-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + # Checkout & Generate + - name: Generate Chinese Readme + run: | + ./generate-playground-cn.sh + mv ./README-CN.md ./README.md + + # Commit + - name: Commit files + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add README.md + git commit -m "Generate Chinese version" + ## Push changes to chinese branch + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: "chinese" + force: true diff --git a/.gitignore b/.gitignore index 697f316..ae30dd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,4 @@ -# CocoaPods -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? -# -# Pods/ - .DS_Store +UserInterfaceState.xcuserstate +playground.xcworkspace +xcuserdata \ No newline at end of file diff --git a/CONTRIBUTING-CN.md b/CONTRIBUTING-CN.md new file mode 100644 index 0000000..9e3b74d --- /dev/null +++ b/CONTRIBUTING-CN.md @@ -0,0 +1,19 @@ +如何贡献(中文版)? +================== + +- 你很棒! +- 仅建议编辑`source-cn`目录中的文件,其余内容是自动生成的并已翻译 +- 提交更改 +- 本地运行`generate-playground-cn.sh` +- 本地打开`Design-Patterns-CN.playground`文件并检查是否正常运行 +- 删除`generate-playground.sh`引起的更改,不要提交它!(_这是最近的更改_) +- 请耐心尊重其他贡献者 + +分支说明 +================== + +英文原上游主仓库已与中文版[统一维护](https://github.com/ochococo/Design-Patterns-In-Swift/pull/93): +- master 为主维护分支 +- chinese 分支仅为方便展示,将 README.md 显示为中文版本,由 GitHub Action 自动驱动 + +因此,直接将 PR 提交至[原始仓库](https://github.com/ochococo/Design-Patterns-In-Swift) master 主分支即可。 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c4b6fb1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,10 @@ +How to contribute? +================== + +- You are awesome! +- Only editing files inside `source` is recommended, the rest is autogenerated and translated +- Commit changes +- Run `generate-playground.sh` locally +- Open the .playground locally and check if it works +- Remove the changes caused by `generate-playground.sh`, do not commit it! (_THIS IS A RECENT CHANGE_) +- Please be patient and respectful to fellow contributors diff --git a/Design-Patterns-CN.playground.zip b/Design-Patterns-CN.playground.zip new file mode 100644 index 0000000..a582e54 Binary files /dev/null and b/Design-Patterns-CN.playground.zip differ diff --git a/Design-Patterns-CN.playground/Pages/Behavioral.xcplaygroundpage/Contents.swift b/Design-Patterns-CN.playground/Pages/Behavioral.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..245070e --- /dev/null +++ b/Design-Patterns-CN.playground/Pages/Behavioral.xcplaygroundpage/Contents.swift @@ -0,0 +1,728 @@ +/*: + + 行为型模式 + ======== + + >在软件工程中, 行为型模式为设计模式的一种类型,用来识别对象之间的常用交流模式并加以实现。如此,可在进行这些交流活动时增强弹性。 + > + >**来源:** [维基百科](https://zh.wikipedia.org/wiki/%E8%A1%8C%E7%82%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F) + +## 目录 + +* [行为型模式](Behavioral) +* [创建型模式](Creational) +* [结构型模式](Structural) +*/ +import Foundation +/*: +🐝 责任链(Chain Of Responsibility) +------------------------------ + +责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。 + +### 示例: +*/ + +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) + } +} +/*: + ### 用法 + */ +// 创建一系列的钱堆,并将其链接起来:10<20<50<100 +let ten = MoneyPile(value: 10, quantity: 6, next: nil) +let twenty = MoneyPile(value: 20, quantity: 2, next: ten) +let fifty = MoneyPile(value: 50, quantity: 2, next: twenty) +let hundred = MoneyPile(value: 100, quantity: 1, next: fifty) + +// 创建 ATM 实例 +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 - 1x100 +/*: +👫 命令(Command) + ------------ + 命令模式是一种设计模式,它尝试以对象来代表实际行动。命令对象可以把行动(action) 及其参数封装起来,于是这些行动可以被: + * 重复多次 + * 取消(如果该对象有实现的话) + * 取消后又再重做 + ### 示例: +*/ +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() + } +} +/*: +### 用法 +*/ +let podBayDoors = "Pod Bay Doors" +let doorModule = HAL9000DoorsOperations(doors:podBayDoors) + +doorModule.open() +doorModule.close() +/*: +🎶 解释器(Interpreter) + ------------------ + + 给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。 + + ### 示例: +*/ + +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) +/*: +🍫 迭代器(Iterator) + --------------- + + 迭代器模式可以让用户通过特定的接口巡访容器中的每一个元素而不用了解底层的实现。 + + ### 示例: +*/ +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)") +} +/*: +💐 中介者(Mediator) + --------------- + + 用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互。 + + ### 示例: +*/ +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) + +/*: +💾 备忘录(Memento) +-------------- + +在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态 + +### 示例: +*/ +typealias Memento = [String: String] +/*: +发起人(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 ] + } +} +/*: +管理者(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) + } +} +/*: +### 用法 +*/ +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) +} +/*: +👓 观察者(Observer) +--------------- + +一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知 + +### 示例: +*/ +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 +/*: +🐉 状态(State) +--------- + +在状态模式中,对象的行为是基于它的内部状态而改变的。 +这个模式允许某个类对象在运行时发生改变。 + +### 示例: +*/ +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) +/*: +💡 策略(Strategy) +-------------- + +对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。策略模式: +* 定义了一族算法(业务规则); +* 封装了每个算法; +* 这族的算法可互换代替(interchangeable)。 + +### 示例: +*/ + +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) + } +} + +/*: + ### 用法 + */ + +let rachel = TestSubject(pupilDiameter: 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) +/*: +📝 模板方法模式 +----------- + + 模板方法模式是一种行为设计模式, 它通过父类/协议中定义了一个算法的框架, 允许子类/具体实现对象在不修改结构的情况下重写算法的特定步骤。 + +### 示例: +*/ +protocol Garden { + func prepareSoil() + func plantSeeds() + func waterPlants() + func prepareGarden() +} + +extension Garden { + + func prepareGarden() { + prepareSoil() + plantSeeds() + waterPlants() + } +} + +final class RoseGarden: Garden { + + func prepare() { + prepareGarden() + } + + func prepareSoil() { + print ("prepare soil for rose garden") + } + + func plantSeeds() { + print ("plant seeds for rose garden") + } + + func waterPlants() { + print ("water the rose garden") + } +} + +/*: +### 用法 +*/ + +let roseGarden = RoseGarden() +roseGarden.prepare() +/*: +🏃 访问者(Visitor) +-------------- + +封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 + +### 示例: +*/ +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" } +} + +/*: +### 用法 +*/ +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 +} + +names diff --git a/Design-Patterns-CN.playground/Pages/Creational.xcplaygroundpage/Contents.swift b/Design-Patterns-CN.playground/Pages/Creational.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..50394f8 --- /dev/null +++ b/Design-Patterns-CN.playground/Pages/Creational.xcplaygroundpage/Contents.swift @@ -0,0 +1,284 @@ +/*: + + 创建型模式 + ======== + + > 创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。 + > + >**来源:** [维基百科](https://zh.wikipedia.org/wiki/%E5%89%B5%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F) + +## 目录 + +* [行为型模式](Behavioral) +* [创建型模式](Creational) +* [结构型模式](Structural) +*/ +import Foundation +/*: +🌰 抽象工厂(Abstract Factory) +------------- + +抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。在正常使用中,客户端程序需要创建抽象工厂的具体实现,然后使用抽象工厂作为接口来创建这一主题的具体对象。 + +### 示例: + +协议 +*/ + +protocol BurgerDescribing { + var ingredients: [String] { get } +} + +struct CheeseBurger: BurgerDescribing { + let ingredients: [String] +} + +protocol BurgerMaking { + func make() -> BurgerDescribing +} + +// 工厂方法实现 + +final class BigKahunaBurger: 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"]) + } +} + +/*: +抽象工厂 +*/ + +enum BurgerFactoryType: BurgerMaking { + + case bigKahuna + case jackInTheBox + + func make() -> BurgerDescribing { + switch self { + case .bigKahuna: + return BigKahunaBurger().make() + case .jackInTheBox: + return JackInTheBox().make() + } + } +} +/*: +### 用法 +*/ +let bigKahuna = BurgerFactoryType.bigKahuna.make() +let jackInTheBox = BurgerFactoryType.jackInTheBox.make() +/*: +👷 生成器(Builder) +-------------- + +一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。 + +### 示例: +*/ +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))" + } +} +/*: +### 用法 +*/ +let empire = DeathStarBuilder { builder in + builder.x = 0.1 + builder.y = 0.2 + builder.z = 0.3 +} + +let deathStar = DeathStar(builder:empire) +/*: +🏭 工厂方法(Factory Method) +----------------------- + +定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。 + +### 示例: +*/ +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 + } + + } +} +/*: +### 用法 +*/ +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 +/*: + 🔂 单态(Monostate) + ------------ + + 单态模式是实现单一共享的另一种方法。不同于单例模式,它通过完全不同的机制,在不限制构造方法的情况下实现单一共享特性。 + 因此,在这种情况下,单态会将状态保存为静态,而不是将整个实例保存为单例。 + [单例和单态 - Robert C. Martin](http://staff.cs.utu.fi/~jounsmed/doos_06/material/SingletonAndMonostate.pdf) + +### 示例: +*/ +class Settings { + + enum Theme { + case `default` + case old + case new + } + + private static var theme: Theme? + + var currentTheme: Theme { + get { Settings.theme ?? .default } + set(newTheme) { Settings.theme = newTheme } + } +} +/*: +### 用法: +*/ +import SwiftUI + +// 改变主题 +let settings = Settings() // 开始使用主题 .old +settings.currentTheme = .new // 改变主题为 .new + +// 界面一 +let screenColor: Color = Settings().currentTheme == .old ? .gray : .white + +// 界面二 +let screenTitle: String = Settings().currentTheme == .old ? "Itunes Connect" : "App Store Connect" +/*: +🃏 原型(Prototype) +-------------- + +通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。 + +### 示例: +*/ +class MoonWorker { + + let name: String + var health: Int = 100 + + init(name: String) { + self.name = name + } + + func clone() -> MoonWorker { + return MoonWorker(name: name) + } +} +/*: +### 用法 +*/ +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 = 0 +/*: +💍 单例(Singleton) +-------------- + +单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为 + +### 示例: +*/ +final class ElonMusk { + + static let shared = ElonMusk() + + private init() { + // Private initialization to ensure just one instance is created. + } +} +/*: +### 用法 +*/ +let elon = ElonMusk.shared // There is only one Elon Musk folks. diff --git a/Design-Patterns-CN.playground/Pages/Index.xcplaygroundpage/Contents.swift b/Design-Patterns-CN.playground/Pages/Index.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..10d89a8 --- /dev/null +++ b/Design-Patterns-CN.playground/Pages/Index.xcplaygroundpage/Contents.swift @@ -0,0 +1,20 @@ +/*: + +设计模式(Swift 5.0 实现) +====================== + +([Design-Patterns-CN.playground.zip](https://raw.githubusercontent.com/ochococo/Design-Patterns-In-Swift/master/Design-Patterns-CN.playground.zip)). + +👷 源项目由 [@nsmeme](http://twitter.com/nsmeme) (Oktawian Chojnacki) 维护。 + +🇨🇳 中文版由 [@binglogo](https://twitter.com/binglogo) 整理翻译。 + +## 目录 + +* [行为型模式](Behavioral) +* [创建型模式](Creational) +* [结构型模式](Structural) +*/ +import Foundation + +print("您好!") diff --git a/Design-Patterns-CN.playground/Pages/Structural.xcplaygroundpage/Contents.swift b/Design-Patterns-CN.playground/Pages/Structural.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..0ee03d7 --- /dev/null +++ b/Design-Patterns-CN.playground/Pages/Structural.xcplaygroundpage/Contents.swift @@ -0,0 +1,405 @@ +/*: + +结构型模式(Structural) +==================== + +> 在软件工程中结构型模式是设计模式,借由一以贯之的方式来了解元件间的关系,以简化设计。 +> +>**来源:** [维基百科](https://zh.wikipedia.org/wiki/%E7%B5%90%E6%A7%8B%E5%9E%8B%E6%A8%A1%E5%BC%8F) + +## 目录 + +* [行为型模式](Behavioral) +* [创建型模式](Creational) +* [结构型模式](Structural) +*/ +import Foundation +/*: +🔌 适配器(Adapter) +-------------- + +适配器模式有时候也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。 + +### 示例: +*/ +protocol NewDeathStarSuperLaserAiming { + var angleV: Double { get } + var angleH: Double { get } +} +/*: +**被适配者** +*/ +struct OldDeathStarSuperlaserTarget { + let angleHorizontal: Float + let angleVertical: Float + + init(angleHorizontal: Float, angleVertical: Float) { + self.angleHorizontal = angleHorizontal + self.angleVertical = angleVertical + } +} +/*: +**适配器** +*/ +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 + } +} +/*: +### 用法 +*/ +let target = OldDeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) +let newFormat = NewDeathStarSuperlaserTarget(target) + +newFormat.angleH +newFormat.angleV +/*: +🌉 桥接(Bridge) +----------- + +桥接模式将抽象部分与实现部分分离,使它们都可以独立的变化。 + +### 示例: +*/ +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") + } +} +/*: +### 用法 +*/ +let tvRemoteControl = RemoteControl(appliance: TV()) +tvRemoteControl.turnOn() + +let fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner()) +fancyVacuumCleanerRemoteControl.turnOn() +/*: +🌿 组合(Composite) +-------------- + +将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 + +### 示例: + +组件(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)") + } +} + +/*: +组合 +*/ +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) + } + } +} +/*: +### 用法 +*/ +var whiteboard = Whiteboard(Circle(), Square()) +whiteboard.draw(fillColor: "Red") +/*: +🍧 修饰(Decorator) +-------------- + +修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。 +就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。 + +### 示例: +*/ +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 ingredients: [String] { + return beverage.ingredients + ["Milk"] + } +} + +struct WhipCoffee: BeverageHaving { + + let beverage: BeverageDataHaving + + var cost: Double { + return beverage.cost + 0.5 + } + + var ingredients: [String] { + return beverage.ingredients + ["Whip"] + } +} +/*: +### 用法 +*/ +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)") +/*: +🎁 外观(Facade) +----------- + +外观模式为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。 + +### 示例: +*/ +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) + } + } +} +/*: +### 用法 +*/ +let storage = Defaults() + +// Store +storage["Bishop"] = "Disconnect me. I’d rather be nothing" + +// Read +storage["Bishop"] +/*: +🍃 享元(Flyweight) +-------------- + +使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。 + +### 示例: +*/ +// 特指咖啡生成的对象会是享元 +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)") + } + } +} +/*: +### 用法 +*/ +let coffeeShop = CoffeeShop(menu: Menu()) + +coffeeShop.takeOrder(origin: "Yirgacheffe, Ethiopia", table: 1) +coffeeShop.takeOrder(origin: "Buziraguhindwa, Burundi", table: 3) + +coffeeShop.serve() +/*: +☔ 保护代理模式(Protection Proxy) +------------------ + +在代理模式中,创建一个类代表另一个底层类的功能。 +保护代理用于限制访问。 + +### 示例: +*/ +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) + } +} +/*: +### 用法 +*/ +let computer = CurrentComputer() +let podBay = "Pod Bay Doors" + +computer.open(doors: podBay) + +computer.authenticate(password: "pass") +computer.open(doors: podBay) +/*: +🍬 虚拟代理(Virtual Proxy) +---------------- + +在代理模式中,创建一个类代表另一个底层类的功能。 +虚拟代理用于对象的需时加载。 + +### 示例: +*/ +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() + } +} +/*: +### 用法 +*/ +let humanInterface = HEVSuitHumanInterface() +humanInterface.administerMorphine() diff --git a/Design-Patterns.playground/Pages/Creational.xcplaygroundpage/timeline.xctimeline b/Design-Patterns-CN.playground/Pages/Structural.xcplaygroundpage/timeline.xctimeline similarity index 100% rename from Design-Patterns.playground/Pages/Creational.xcplaygroundpage/timeline.xctimeline rename to Design-Patterns-CN.playground/Pages/Structural.xcplaygroundpage/timeline.xctimeline diff --git a/Design-Patterns-CN.playground/contents.xcplayground b/Design-Patterns-CN.playground/contents.xcplayground new file mode 100644 index 0000000..9c4add8 --- /dev/null +++ b/Design-Patterns-CN.playground/contents.xcplayground @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Design-Patterns.playground.zip b/Design-Patterns.playground.zip index 80a2654..e4163de 100644 Binary files a/Design-Patterns.playground.zip and b/Design-Patterns.playground.zip differ diff --git a/Design-Patterns.playground/Pages/Behavioral.xcplaygroundpage/Contents.swift b/Design-Patterns.playground/Pages/Behavioral.xcplaygroundpage/Contents.swift index ea11058..1a013ab 100644 --- a/Design-Patterns.playground/Pages/Behavioral.xcplaygroundpage/Contents.swift +++ b/Design-Patterns.playground/Pages/Behavioral.xcplaygroundpage/Contents.swift @@ -1,15 +1,19 @@ -//: Behavioral | -//: [Creational](Creational) | -//: [Structural](Structural) /*: + Behavioral ========== >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](http://en.wikipedia.org/wiki/Behavioral_pattern) + +## Table of Contents + +* [Behavioral](Behavioral) +* [Creational](Creational) +* [Structural](Structural) + */ -import Swift import Foundation /*: 🐝 Chain Of Responsibility @@ -19,90 +23,94 @@ The chain of responsibility pattern is used to process varied requests, each of ### Example: */ -class MoneyPile { + +protocol Withdrawing { + func withdraw(amount: Int) -> Bool +} + +final class MoneyPile: Withdrawing { + let value: Int var quantity: Int - var nextPile: MoneyPile? - - init(value: Int, quantity: Int, nextPile: MoneyPile?) { + var next: Withdrawing? + + init(value: Int, quantity: Int, next: Withdrawing?) { self.value = value self.quantity = quantity - self.nextPile = nextPile + self.next = next } - - func canWithdraw(v: Int) -> Bool { - var v = v + func withdraw(amount: Int) -> Bool { + + var amount = amount func canTakeSomeBill(want: Int) -> Bool { return (want / self.value) > 0 } - - var q = self.quantity - while canTakeSomeBill(v) { + var quantity = self.quantity + + while canTakeSomeBill(want: amount) { - if q == 0 { + if quantity == 0 { break } - v -= self.value - q -= 1 + amount -= self.value + quantity -= 1 } - if v == 0 { + guard amount > 0 else { return true - } else if let next = self.nextPile { - return next.canWithdraw(v) + } + + if let next = self.next { + return next.withdraw(amount: amount) } return false } } -class ATM { - private var hundred: MoneyPile - private var fifty: MoneyPile - private var twenty: MoneyPile - private var ten: MoneyPile - - private var startPile: MoneyPile { +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: MoneyPile, - fifty: MoneyPile, - twenty: MoneyPile, - ten: MoneyPile) { + + init(hundred: Withdrawing, + fifty: Withdrawing, + twenty: Withdrawing, + ten: Withdrawing) { self.hundred = hundred self.fifty = fifty self.twenty = twenty self.ten = ten } - - func canWithdraw(value: Int) -> String { - return "Can withdraw: \(self.startPile.canWithdraw(value))" + + func withdraw(amount: Int) -> Bool { + return startPile.withdraw(amount: amount) } } /*: ### Usage */ // 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) +let ten = MoneyPile(value: 10, quantity: 6, next: nil) +let twenty = MoneyPile(value: 20, quantity: 2, next: ten) +let fifty = MoneyPile(value: 50, quantity: 2, next: twenty) +let hundred = MoneyPile(value: 100, quantity: 1, next: fifty) // Build ATM. var atm = ATM(hundred: hundred, fifty: fifty, twenty: twenty, ten: ten) -atm.canWithdraw(310) // Cannot because ATM has only 300 -atm.canWithdraw(100) // Can withdraw - 1x100 -atm.canWithdraw(165) // Cannot withdraw because ATM doesn't has bill with value of 5 -atm.canWithdraw(30) // Can withdraw - 1x20, 2x10 -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Chain-Of-Responsibility) -*/ +atm.withdraw(amount: 310) // Cannot because ATM has only 300 +atm.withdraw(amount: 100) // Can withdraw - 1x100 /*: 👫 Command ---------- @@ -115,7 +123,7 @@ protocol DoorCommand { func execute() -> String } -class OpenCommand : DoorCommand { +final class OpenCommand: DoorCommand { let doors:String required init(doors: String) { @@ -127,7 +135,7 @@ class OpenCommand : DoorCommand { } } -class CloseCommand : DoorCommand { +final class CloseCommand: DoorCommand { let doors:String required init(doors: String) { @@ -139,7 +147,7 @@ class CloseCommand : DoorCommand { } } -class HAL9000DoorsOperations { +final class HAL9000DoorsOperations { let openCommand: DoorCommand let closeCommand: DoorCommand @@ -173,90 +181,86 @@ The interpreter pattern is used to evaluate sentences in a language. ### Example */ -protocol IntegerExp { - func evaluate(context: IntegerContext) -> Int - func replace(character: Character, integerExp: IntegerExp) -> IntegerExp - func copy() -> IntegerExp +protocol IntegerExpression { + func evaluate(_ context: IntegerContext) -> Int + func replace(character: Character, integerExpression: IntegerExpression) -> IntegerExpression + func copied() -> IntegerExpression } -class IntegerContext { +final class IntegerContext { private var data: [Character:Int] = [:] - + func lookup(name: Character) -> Int { return self.data[name]! } - - func assign(integerVarExp: IntegerVarExp, value: Int) { - self.data[integerVarExp.name] = value + + func assign(expression: IntegerVariableExpression, value: Int) { + self.data[expression.name] = value } } -class IntegerVarExp: IntegerExp { +final class IntegerVariableExpression: IntegerExpression { let name: Character - + init(name: Character) { self.name = name } - - func evaluate(context: IntegerContext) -> Int { - return context.lookup(self.name) + + func evaluate(_ context: IntegerContext) -> Int { + return context.lookup(name: self.name) } - - func replace(name: Character, integerExp: IntegerExp) -> IntegerExp { + + func replace(character name: Character, integerExpression: IntegerExpression) -> IntegerExpression { if name == self.name { - return integerExp.copy() + return integerExpression.copied() } else { - return IntegerVarExp(name: self.name) + return IntegerVariableExpression(name: self.name) } } - - func copy() -> IntegerExp { - return IntegerVarExp(name: self.name) + + func copied() -> IntegerExpression { + return IntegerVariableExpression(name: self.name) } } -class AddExp: IntegerExp { - private var operand1: IntegerExp - private var operand2: IntegerExp - - init(op1: IntegerExp, op2: IntegerExp) { +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 { + + func evaluate(_ context: IntegerContext) -> Int { return self.operand1.evaluate(context) + self.operand2.evaluate(context) } - - func replace(character: Character, integerExp: IntegerExp) -> IntegerExp { - return AddExp(op1: operand1.replace(character, integerExp: integerExp), - op2: operand2.replace(character, integerExp: integerExp)) + + func replace(character: Character, integerExpression: IntegerExpression) -> IntegerExpression { + return AddExpression(op1: operand1.replace(character: character, integerExpression: integerExpression), + op2: operand2.replace(character: character, integerExpression: integerExpression)) } - - func copy() -> IntegerExp { - return AddExp(op1: self.operand1, op2: self.operand2) + + func copied() -> IntegerExpression { + return AddExpression(op1: self.operand1, op2: self.operand2) } } /*: ### Usage */ -var expression: IntegerExp? -var intContext = IntegerContext() +var context = IntegerContext() -var a = IntegerVarExp(name: "A") -var b = IntegerVarExp(name: "B") -var c = IntegerVarExp(name: "C") +var a = IntegerVariableExpression(name: "A") +var b = IntegerVariableExpression(name: "B") +var c = IntegerVariableExpression(name: "C") -expression = AddExp(op1: a, op2: AddExp(op1: b, op2: c)) // a + (b + c) +var expression = AddExpression(op1: a, op2: AddExpression(op1: b, op2: c)) // a + (b + c) -intContext.assign(a, value: 2) -intContext.assign(b, value: 1) -intContext.assign(c, value: 3) +context.assign(expression: a, value: 2) +context.assign(expression: b, value: 1) +context.assign(expression: c, value: 3) -var result = expression?.evaluate(intContext) -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Interpreter) -*/ +var result = expression.evaluate(context) /*: 🍫 Iterator ----------- @@ -265,22 +269,38 @@ The iterator pattern is used to provide a standard interface for traversing a co ### Example: */ -struct NovellasCollection { - let novellas: [T] +struct Novella { + let name: String } -extension NovellasCollection: SequenceType { - typealias Generator = AnyGenerator - - func generate() -> AnyGenerator { - var i = 0 - return AnyGenerator { i += 1; return i >= self.novellas.count ? nil : self.novellas[i] } +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) } } /*: ### Usage */ -let greatNovellas = NovellasCollection(novellas:["Mist"]) +let greatNovellas = Novellas(novellas: [Novella(name: "The Mist")] ) for novella in greatNovellas { print("I've read: \(novella)") @@ -293,65 +313,62 @@ The mediator pattern is used to reduce coupling between classes that communicate ### Example */ +protocol Receiver { + associatedtype MessageType + func receive(message: MessageType) +} -class Colleague { +protocol Sender { + associatedtype MessageType + associatedtype ReceiverType: Receiver + + var recipients: [ReceiverType] { get } + + func send(message: MessageType) +} + +struct Programmer: Receiver { let name: String - let mediator: Mediator - init(name: String, mediator: Mediator) { + init(name: String) { self.name = name - self.mediator = mediator - } - - func send(message: String) { - mediator.send(message, colleague: self) } func receive(message: String) { - assert(false, "Method should be overriden") + print("\(name) received: \(message)") } } -protocol Mediator { - func send(message: String, colleague: Colleague) -} - -class MessageMediator: Mediator { - private var colleagues: [Colleague] = [] +final class MessageMediator: Sender { + internal var recipients: [Programmer] = [] - func addColleague(colleague: Colleague) { - colleagues.append(colleague) + func add(recipient: Programmer) { + recipients.append(recipient) } - func send(message: String, colleague: Colleague) { - for c in colleagues { - if c !== colleague { //for simplicity we compare object references - c.receive(message) - } + func send(message: String) { + for recipient in recipients { + recipient.receive(message: message) } } } -class ConcreteColleague: Colleague { - override func receive(message: String) { - print("Colleague \(name) received: \(message)") - } -} - /*: ### Usage */ +func spamMonster(message: String, worker: MessageMediator) { + worker.send(message: message) +} let messagesMediator = MessageMediator() -let user0 = ConcreteColleague(name: "0", mediator: messagesMediator) -let user1 = ConcreteColleague(name: "1", mediator: messagesMediator) -messagesMediator.addColleague(user0) -messagesMediator.addColleague(user1) -user0.send("Hello") // user1 receives message -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Mediator) -*/ +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) + /*: 💾 Memento ---------- @@ -360,103 +377,119 @@ The memento pattern is used to capture the current state of an object and store ### Example */ -typealias Memento = Dictionary - -let DPMementoKeyChapter = "com.valve.halflife.chapter" -let DPMementoKeyWeapon = "com.valve.halflife.weapon" -let DPMementoGameState = "com.valve.halflife.state" +typealias Memento = [String: String] /*: Originator */ -class GameState { - var chapter: String = "" - var weapon: String = "" +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" + } - func toMemento() -> Memento { - return [ DPMementoKeyChapter:chapter, DPMementoKeyWeapon: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 } - func restoreFromMemento(memento: Memento) { - chapter = memento[DPMementoKeyChapter] as? String ?? "n/a" - weapon = memento[DPMementoKeyWeapon] as? String ?? "n/a" + var memento: Memento { + return [ Keys.chapter: chapter, Keys.weapon: weapon ] } } /*: Caretaker */ enum CheckPoint { - static func saveState(memento: Memento, keyName: String = DPMementoGameState) { - let defaults = NSUserDefaults.standardUserDefaults() - defaults.setObject(memento, forKey: keyName) + + private static let defaults = UserDefaults.standard + + static func save(_ state: MementoConvertible, saveName: String) { + defaults.set(state.memento, forKey: saveName) defaults.synchronize() } - static func restorePreviousState(keyName keyName: String = DPMementoGameState) -> Memento { - let defaults = NSUserDefaults.standardUserDefaults() - - return defaults.objectForKey(keyName) as? Memento ?? Memento() + static func restore(saveName: String) -> Any? { + return defaults.object(forKey: saveName) } } /*: - ### Usage +### Usage */ -var gameState = GameState() -gameState.restoreFromMemento(CheckPoint.restorePreviousState()) - -gameState.chapter = "Black Mesa Inbound" -gameState.weapon = "Crowbar" -CheckPoint.saveState(gameState.toMemento()) +var gameState = GameState(chapter: "Black Mesa Inbound", weapon: "Crowbar") gameState.chapter = "Anomalous Materials" gameState.weapon = "Glock 17" -gameState.restoreFromMemento(CheckPoint.restorePreviousState()) +CheckPoint.save(gameState, saveName: "gameState1") gameState.chapter = "Unforeseen Consequences" gameState.weapon = "MP5" -CheckPoint.saveState(gameState.toMemento(), keyName: "gameState2") +CheckPoint.save(gameState, saveName: "gameState2") gameState.chapter = "Office Complex" gameState.weapon = "Crossbow" -CheckPoint.saveState(gameState.toMemento()) - -gameState.restoreFromMemento(CheckPoint.restorePreviousState(keyName: "gameState2")) +CheckPoint.save(gameState, saveName: "gameState3") +if let memento = CheckPoint.restore(saveName: "gameState1") as? Memento { + let finalState = GameState(memento: memento) + dump(finalState) +} /*: 👓 Observer ----------- -The observer pattern is used to allow an object to publish changes to its state. +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. ### Example */ protocol PropertyObserver : class { - func willChangePropertyName(propertyName:String, newPropertyValue:AnyObject?) - func didChangePropertyName(propertyName:String, oldPropertyValue:AnyObject?) + func willChange(propertyName: String, newPropertyValue: Any?) + func didChange(propertyName: String, oldPropertyValue: Any?) } -class TestChambers { +final class TestChambers { weak var observer:PropertyObserver? + private let testChamberNumberName = "testChamberNumber" + var testChamberNumber: Int = 0 { willSet(newValue) { - observer?.willChangePropertyName("testChamberNumber", newPropertyValue:newValue) + observer?.willChange(propertyName: testChamberNumberName, newPropertyValue: newValue) } didSet { - observer?.didChangePropertyName("testChamberNumber", oldPropertyValue:oldValue) + observer?.didChange(propertyName: testChamberNumberName, oldPropertyValue: oldValue) } } } -class Observer : PropertyObserver { - func willChangePropertyName(propertyName: String, newPropertyValue: AnyObject?) { +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 didChangePropertyName(propertyName: String, oldPropertyValue: AnyObject?) { + 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.") } @@ -468,38 +501,34 @@ class Observer : PropertyObserver { var observerInstance = Observer() var testChambers = TestChambers() testChambers.observer = observerInstance -testChambers.testChamberNumber++ -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Observer) -*/ +testChambers.testChamberNumber += 1 /*: 🐉 State --------- -The state pattern is used to alter the behaviour of an object as its internal state changes. +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. ### Example */ -class Context { +final class Context { private var state: State = UnauthorizedState() var isAuthorized: Bool { - get { return state.isAuthorized(self) } + get { return state.isAuthorized(context: self) } } var userId: String? { - get { return state.userId(self) } + get { return state.userId(context: self) } } - func changeStateToAuthorized(userId userId: String) { + func changeStateToAuthorized(userId: String) { state = AuthorizedState(userId: userId) } func changeStateToUnauthorized() { state = UnauthorizedState() } - } protocol State { @@ -525,15 +554,12 @@ class AuthorizedState: State { /*: ### Usage */ -let context = Context() -(context.isAuthorized, context.userId) -context.changeStateToAuthorized(userId: "admin") -(context.isAuthorized, context.userId) // now logged in as "admin" -context.changeStateToUnauthorized() -(context.isAuthorized, context.userId) -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-State) -*/ +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) /*: 💡 Strategy ----------- @@ -542,45 +568,105 @@ The strategy pattern is used to create an interchangeable family of algorithms f ### Example */ -protocol PrintStrategy { - func printString(string: String) -> String + +struct TestSubject { + let pupilDiameter: Double + let blushResponse: Double + let isOrganic: Bool } -class Printer { +protocol RealnessTesting: AnyObject { + func testRealness(_ testSubject: TestSubject) -> Bool +} - let strategy: PrintStrategy - - func printString(string: String) -> String { - return self.strategy.printString(string) - } - - init(strategy: PrintStrategy) { - self.strategy = strategy +final class VoightKampffTest: RealnessTesting { + func testRealness(_ testSubject: TestSubject) -> Bool { + return testSubject.pupilDiameter < 30.0 || testSubject.blushResponse == 0.0 } } -class UpperCaseStrategy : PrintStrategy { - func printString(string:String) -> String { - return string.uppercaseString +final class GeneticTest: RealnessTesting { + func testRealness(_ testSubject: TestSubject) -> Bool { + return testSubject.isOrganic } } -class LowerCaseStrategy : PrintStrategy { - func printString(string:String) -> String { - return string.lowercaseString +final class BladeRunner { + private let strategy: RealnessTesting + + init(test: RealnessTesting) { + self.strategy = test + } + + func testIfAndroid(_ testSubject: TestSubject) -> Bool { + return !strategy.testRealness(testSubject) } } + /*: -### Usage + ### Usage + */ + +let rachel = TestSubject(pupilDiameter: 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) +/*: +📝 Template Method +----------- + + The template method pattern defines the steps of an algorithm and allows the redefinition of one or more of these steps. In this way, the template method protects the algorithm, the order of execution and provides abstract methods that can be implemented by concrete types. + +### Example */ -var lower = Printer(strategy:LowerCaseStrategy()) -lower.printString("O tempora, o mores!") +protocol Garden { + func prepareSoil() + func plantSeeds() + func waterPlants() + func prepareGarden() +} + +extension Garden { + + func prepareGarden() { + prepareSoil() + plantSeeds() + waterPlants() + } +} + +final class RoseGarden: Garden { + + func prepare() { + prepareGarden() + } + + func prepareSoil() { + print ("prepare soil for rose garden") + } + + func plantSeeds() { + print ("plant seeds for rose garden") + } + + func waterPlants() { + print ("water the rose garden") + } +} -var upper = Printer(strategy:UpperCaseStrategy()) -upper.printString("O tempora, o mores!") /*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Strategy) +### Usage */ + +let roseGarden = RoseGarden() +roseGarden.prepare() /*: 🏃 Visitor ---------- @@ -593,41 +679,48 @@ protocol PlanetVisitor { func visit(planet: PlanetAlderaan) func visit(planet: PlanetCoruscant) func visit(planet: PlanetTatooine) + func visit(planet: MoonJedha) } protocol Planet { func accept(visitor: PlanetVisitor) } -class PlanetAlderaan: Planet { - func accept(visitor: PlanetVisitor) { visitor.visit(self) } +final class MoonJedha: Planet { + func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) } } -class PlanetCoruscant: Planet { - func accept(visitor: PlanetVisitor) { visitor.visit(self) } + +final class PlanetAlderaan: Planet { + func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) } } -class PlanetTatooine: Planet { - func accept(visitor: PlanetVisitor) { visitor.visit(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) } } -class NameVisitor: PlanetVisitor { +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" } } + /*: ### Usage */ -let planets: [Planet] = [PlanetAlderaan(), PlanetCoruscant(), PlanetTatooine()] +let planets: [Planet] = [PlanetAlderaan(), PlanetCoruscant(), PlanetTatooine(), MoonJedha()] let names = planets.map { (planet: Planet) -> String in let visitor = NameVisitor() - planet.accept(visitor) - return visitor.name + planet.accept(visitor: visitor) + + return visitor.name } names -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Visitor) -*/ diff --git a/Design-Patterns.playground/Pages/Creational.xcplaygroundpage/Contents.swift b/Design-Patterns.playground/Pages/Creational.xcplaygroundpage/Contents.swift index 45c1e1d..0cf5a4d 100644 --- a/Design-Patterns.playground/Pages/Creational.xcplaygroundpage/Contents.swift +++ b/Design-Patterns.playground/Pages/Creational.xcplaygroundpage/Contents.swift @@ -1,15 +1,19 @@ -//: [Behavioral](Behavioral) | -//: Creational | -//: [Structural](Structural) /*: + Creational ========== > 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](http://en.wikipedia.org/wiki/Creational_pattern) + +## Table of Contents + +* [Behavioral](Behavioral) +* [Creational](Creational) +* [Structural](Structural) + */ -import Swift import Foundation /*: 🌰 Abstract Factory @@ -19,68 +23,59 @@ The abstract factory pattern is used to provide a client with a set of related o The "family" of objects created by the factory are determined at run-time. ### Example -*/ -/*: + Protocols */ -protocol Decimal { - func stringValue() -> String - // factory - static func make(string : String) -> Decimal + +protocol BurgerDescribing { + var ingredients: [String] { get } } -typealias NumberFactory = (String) -> Decimal +struct CheeseBurger: BurgerDescribing { + let ingredients: [String] +} -// Number implementations with factory methods +protocol BurgerMaking { + func make() -> BurgerDescribing +} -struct NextStepNumber : Decimal { - private var nextStepNumber : NSNumber +// Number implementations with factory methods - func stringValue() -> String { return nextStepNumber.stringValue } - - // factory - static func make(string : String) -> Decimal { - return NextStepNumber(nextStepNumber:NSNumber(longLong:(string as NSString).longLongValue)) +final class BigKahunaBurger: BurgerMaking { + func make() -> BurgerDescribing { + return CheeseBurger(ingredients: ["Cheese", "Burger", "Lettuce", "Tomato"]) } } -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) +final class JackInTheBox: BurgerMaking { + func make() -> BurgerDescribing { + return CheeseBurger(ingredients: ["Cheese", "Burger", "Tomato", "Onions"]) } } + /*: Abstract factory */ -enum NumberType { - case NextStep, Swift -} -enum NumberHelper { - static func factoryFor(type : NumberType) -> NumberFactory { - switch type { - case .NextStep: - return NextStepNumber.make - case .Swift: - return SwiftNumber.make +enum BurgerFactoryType: BurgerMaking { + + case bigKahuna + case jackInTheBox + + func make() -> BurgerDescribing { + switch self { + case .bigKahuna: + return BigKahunaBurger().make() + case .jackInTheBox: + return JackInTheBox().make() } } } /*: ### Usage */ -let factoryOne = NumberHelper.factoryFor(.NextStep) -let numberOne = factoryOne("1") -numberOne.stringValue() - -let factoryTwo = NumberHelper.factoryFor(.Swift) -let numberTwo = factoryTwo("2") -numberTwo.stringValue() +let bigKahuna = BurgerFactoryType.bigKahuna.make() +let jackInTheBox = BurgerFactoryType.jackInTheBox.make() /*: 👷 Builder ---------- @@ -90,7 +85,7 @@ An external class controls the construction algorithm. ### Example */ -class DeathStarBuilder { +final class DeathStarBuilder { var x: Double? var y: Double? @@ -111,7 +106,7 @@ struct DeathStar : CustomStringConvertible { init?(builder: DeathStarBuilder) { - if let x = builder.x, y = builder.y, z = builder.z { + if let x = builder.x, let y = builder.y, let z = builder.z { self.x = x self.y = y self.z = z @@ -135,9 +130,6 @@ let empire = DeathStarBuilder { builder in let deathStar = DeathStar(builder:empire) /*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Builder) -*/ -/*: 🏭 Factory Method ----------------- @@ -145,42 +137,45 @@ The factory pattern is used to replace class constructors, abstracting the proce ### Example */ -protocol Currency { - func symbol() -> String - func code() -> String +protocol CurrencyDescribing { + var symbol: String { get } + var code: String { get } } -class Euro : Currency { - func symbol() -> String { +final class Euro: CurrencyDescribing { + var symbol: String { return "€" } - func code() -> String { + var code: String { return "EUR" } } -class UnitedStatesDolar : Currency { - func symbol() -> String { +final class UnitedStatesDolar: CurrencyDescribing { + var symbol: String { return "$" } - func code() -> String { + var code: String { return "USD" } } enum Country { - case UnitedStates, Spain, UK, Greece + case unitedStates + case spain + case uk + case greece } enum CurrencyFactory { - static func currencyForCountry(country:Country) -> Currency? { + static func currency(for country: Country) -> CurrencyDescribing? { switch country { - case .Spain, .Greece : + case .spain, .greece: return Euro() - case .UnitedStates : + case .unitedStates: return UnitedStatesDolar() default: return nil @@ -193,10 +188,50 @@ enum CurrencyFactory { */ let noCurrencyCode = "No Currency Code Available" -CurrencyFactory.currencyForCountry(.Greece)?.code() ?? noCurrencyCode -CurrencyFactory.currencyForCountry(.Spain)?.code() ?? noCurrencyCode -CurrencyFactory.currencyForCountry(.UnitedStates)?.code() ?? noCurrencyCode -CurrencyFactory.currencyForCountry(.UK)?.code() ?? noCurrencyCode +CurrencyFactory.currency(for: .greece)?.code ?? noCurrencyCode +CurrencyFactory.currency(for: .spain)?.code ?? noCurrencyCode +CurrencyFactory.currency(for: .unitedStates)?.code ?? noCurrencyCode +CurrencyFactory.currency(for: .uk)?.code ?? noCurrencyCode +/*: + 🔂 Monostate + ------------ + + The monostate pattern is another way to achieve singularity. It works through a completely different mechanism, it enforces the behavior of singularity without imposing structural constraints. + So in that case, monostate saves the state as static instead of the entire instance as a singleton. + [SINGLETON and MONOSTATE - Robert C. Martin](http://staff.cs.utu.fi/~jounsmed/doos_06/material/SingletonAndMonostate.pdf) + +### Example: +*/ +class Settings { + + enum Theme { + case `default` + case old + case new + } + + private static var theme: Theme? + + var currentTheme: Theme { + get { Settings.theme ?? .default } + set(newTheme) { Settings.theme = newTheme } + } +} +/*: +### Usage: +*/ + +import SwiftUI + +// When change the theme +let settings = Settings() // Starts using theme .old +settings.currentTheme = .new // Change theme to .new + +// On screen 1 +let screenColor: Color = Settings().currentTheme == .old ? .gray : .white + +// On screen 2 +let screenTitle: String = Settings().currentTheme == .old ? "Itunes Connect" : "App Store Connect" /*: 🃏 Prototype ------------ @@ -206,34 +241,32 @@ This practise is particularly useful when the construction of a new object is in ### Example */ -class ChungasRevengeDisplay { - var name: String? - let font: String +class MoonWorker { + + let name: String + var health: Int = 100 - init(font: String) { - self.font = font + init(name: String) { + self.name = name } - func clone() -> ChungasRevengeDisplay { - return ChungasRevengeDisplay(font:self.font) + func clone() -> MoonWorker { + return MoonWorker(name: name) } } /*: ### Usage */ -let Prototype = ChungasRevengeDisplay(font:"GotanProject") +let prototype = MoonWorker(name: "Sam Bell") -let Philippe = Prototype.clone() -Philippe.name = "Philippe" +var bell1 = prototype.clone() +bell1.health = 12 -let Christoph = Prototype.clone() -Christoph.name = "Christoph" +var bell2 = prototype.clone() +bell2.health = 23 -let Eduardo = Prototype.clone() -Eduardo.name = "Eduardo" -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Prototype) -*/ +var bell3 = prototype.clone() +bell3.health = 0 /*: 💍 Singleton ------------ @@ -244,8 +277,9 @@ There are very few applications, do not overuse this pattern! ### Example: */ -class DeathStarSuperlaser { - static let sharedInstance = DeathStarSuperlaser() +final class ElonMusk { + + static let shared = ElonMusk() private init() { // Private initialization to ensure just one instance is created. @@ -254,4 +288,4 @@ class DeathStarSuperlaser { /*: ### Usage: */ -let laser = DeathStarSuperlaser.sharedInstance +let elon = ElonMusk.shared // There is only one Elon Musk folks. diff --git a/Design-Patterns.playground/Pages/Index.xcplaygroundpage/Contents.swift b/Design-Patterns.playground/Pages/Index.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..0bb8050 --- /dev/null +++ b/Design-Patterns.playground/Pages/Index.xcplaygroundpage/Contents.swift @@ -0,0 +1,25 @@ +/*: + +Design Patterns implemented in Swift 5.0 +======================================== + +A short cheat-sheet with Xcode 10.2 Playground ([Design-Patterns.playground.zip](https://raw.githubusercontent.com/ochococo/Design-Patterns-In-Swift/master/Design-Patterns.playground.zip)). + +### [🇨🇳中文版](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/README-CN.md) + +👷 Project started by: [@nsmeme](http://twitter.com/nsmeme) (Oktawian Chojnacki) + +👷 中文版由 [@binglogo](https://twitter.com/binglogo) (棒棒彬) 整理翻译。 + +🚀 How to generate README, Playground and zip from source: [GENERATE.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/GENERATE.md) + +## Table of Contents + +* [Behavioral](Behavioral) +* [Creational](Creational) +* [Structural](Structural) + +*/ +import Foundation + +print("Welcome!") diff --git a/Design-Patterns.playground/Pages/Structural.xcplaygroundpage/Contents.swift b/Design-Patterns.playground/Pages/Structural.xcplaygroundpage/Contents.swift index 560de98..29c0ef1 100644 --- a/Design-Patterns.playground/Pages/Structural.xcplaygroundpage/Contents.swift +++ b/Design-Patterns.playground/Pages/Structural.xcplaygroundpage/Contents.swift @@ -1,15 +1,19 @@ -//: [Behavioral](Behavioral) | -//: [Creational](Creational) | -//: Structural /*: + 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](http://en.wikipedia.org/wiki/Structural_pattern) + +## Table of Contents + +* [Behavioral](Behavioral) +* [Creational](Creational) +* [Structural](Structural) + */ -import Swift import Foundation /*: 🔌 Adapter @@ -19,18 +23,18 @@ The adapter pattern is used to provide a link between two otherwise incompatible ### Example */ -protocol OlderDeathStarSuperLaserAiming { - var angleV: NSNumber {get} - var angleH: NSNumber {get} +protocol NewDeathStarSuperLaserAiming { + var angleV: Double { get } + var angleH: Double { get } } /*: **Adaptee** */ -struct DeathStarSuperlaserTarget { - let angleHorizontal: Double - let angleVertical: Double +struct OldDeathStarSuperlaserTarget { + let angleHorizontal: Float + let angleVertical: Float - init(angleHorizontal:Double, angleVertical:Double) { + init(angleHorizontal: Float, angleVertical: Float) { self.angleHorizontal = angleHorizontal self.angleVertical = angleVertical } @@ -38,32 +42,30 @@ struct DeathStarSuperlaserTarget { /*: **Adapter** */ -struct OldDeathStarSuperlaserTarget : OlderDeathStarSuperLaserAiming { - private let target : DeathStarSuperlaserTarget +struct NewDeathStarSuperlaserTarget: NewDeathStarSuperLaserAiming { - var angleV:NSNumber { - return NSNumber(double: target.angleVertical) + private let target: OldDeathStarSuperlaserTarget + + var angleV: Double { + return Double(target.angleVertical) } - var angleH:NSNumber { - return NSNumber(double: target.angleHorizontal) + var angleH: Double { + return Double(target.angleHorizontal) } - init(_ target:DeathStarSuperlaserTarget) { + init(_ target: OldDeathStarSuperlaserTarget) { self.target = target } } /*: ### Usage */ -let target = DeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) -let oldFormat = OldDeathStarSuperlaserTarget(target) +let target = OldDeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) +let newFormat = NewDeathStarSuperlaserTarget(target) -oldFormat.angleH -oldFormat.angleV -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Adapter) -*/ +newFormat.angleH +newFormat.angleV /*: 🌉 Bridge ---------- @@ -73,7 +75,7 @@ The bridge pattern is used to separate the abstract elements of a class from the ### Example */ protocol Switch { - var appliance: Appliance {get set} + var appliance: Appliance { get set } func turnOn() } @@ -81,7 +83,7 @@ protocol Appliance { func run() } -class RemoteControl: Switch { +final class RemoteControl: Switch { var appliance: Appliance func turnOn() { @@ -93,13 +95,13 @@ class RemoteControl: Switch { } } -class TV: Appliance { +final class TV: Appliance { func run() { print("tv turned on"); } } -class VacuumCleaner: Appliance { +final class VacuumCleaner: Appliance { func run() { print("vacuum cleaner turned on") } @@ -107,10 +109,10 @@ class VacuumCleaner: Appliance { /*: ### Usage */ -var tvRemoteControl = RemoteControl(appliance: TV()) +let tvRemoteControl = RemoteControl(appliance: TV()) tvRemoteControl.turnOn() -var fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner()) +let fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner()) fancyVacuumCleanerRemoteControl.turnOn() /*: 🌿 Composite @@ -119,23 +121,22 @@ 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. ### Example -*/ -/*: + Component */ protocol Shape { func draw(fillColor: String) } -/*: +/*: Leafs -*/ -class Square : Shape { +*/ +final class Square: Shape { func draw(fillColor: String) { print("Drawing a Square with color \(fillColor)") } } -class Circle : Shape { +final class Circle: Shape { func draw(fillColor: String) { print("Drawing a circle with color \(fillColor)") } @@ -144,16 +145,17 @@ class Circle : Shape { /*: Composite */ -class Whiteboard : Shape { - lazy var shapes = [Shape]() - - init(_ shapes:Shape...) { +final class Whiteboard: Shape { + + private lazy var shapes = [Shape]() + + init(_ shapes: Shape...) { self.shapes = shapes } - - func draw(fillColor:String) { + + func draw(fillColor: String) { for shape in self.shapes { - shape.draw(fillColor) + shape.draw(fillColor: fillColor) } } } @@ -161,7 +163,7 @@ class Whiteboard : Shape { ### Usage: */ var whiteboard = Whiteboard(Circle(), Square()) -whiteboard.draw("Red") +whiteboard.draw(fillColor: "Red") /*: 🍧 Decorator ------------ @@ -171,74 +173,59 @@ This provides a flexible alternative to using inheritance to modify behaviour. ### Example */ -protocol Coffee { - func getCost() -> Double - func getIngredients() -> String +protocol CostHaving { + var cost: Double { get } } -class SimpleCoffee: Coffee { - func getCost() -> Double { - return 1.0 - } - - func getIngredients() -> String { - return "Coffee" - } +protocol IngredientsHaving { + var ingredients: [String] { get } } -class CoffeeDecorator: Coffee { - private let decoratedCoffee: Coffee - private let ingredientSeparator: String = ", " - - required init(decoratedCoffee: Coffee) { - self.decoratedCoffee = decoratedCoffee - } +typealias BeverageDataHaving = CostHaving & IngredientsHaving - func getCost() -> Double { - return decoratedCoffee.getCost() - } +struct SimpleCoffee: BeverageDataHaving { + let cost: Double = 1.0 + let ingredients = ["Water", "Coffee"] +} - func getIngredients() -> String { - return decoratedCoffee.getIngredients() - } +protocol BeverageHaving: BeverageDataHaving { + var beverage: BeverageDataHaving { get } } -class Milk: CoffeeDecorator { - required init(decoratedCoffee: Coffee) { - super.init(decoratedCoffee: decoratedCoffee) - } +struct Milk: BeverageHaving { + + let beverage: BeverageDataHaving - override func getCost() -> Double { - return super.getCost() + 0.5 + var cost: Double { + return beverage.cost + 0.5 } - override func getIngredients() -> String { - return super.getIngredients() + ingredientSeparator + "Milk" + var ingredients: [String] { + return beverage.ingredients + ["Milk"] } } -class WhipCoffee: CoffeeDecorator { - required init(decoratedCoffee: Coffee) { - super.init(decoratedCoffee: decoratedCoffee) - } +struct WhipCoffee: BeverageHaving { + + let beverage: BeverageDataHaving - override func getCost() -> Double { - return super.getCost() + 0.7 + var cost: Double { + return beverage.cost + 0.5 } - override func getIngredients() -> String { - return super.getIngredients() + ingredientSeparator + "Whip" + var ingredients: [String] { + return beverage.ingredients + ["Whip"] } } /*: ### Usage: */ -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())") +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)") /*: 🎁 Façade --------- @@ -247,26 +234,89 @@ The facade pattern is used to define a simplified interface to a more complex su ### Example */ -enum Eternal { +final class Defaults { - static func setObject(value: AnyObject!, forKey defaultName: String!) { - let defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults() - defaults.setObject(value, forKey:defaultName) - defaults.synchronize() + private let defaults: UserDefaults + + init(defaults: UserDefaults = .standard) { + self.defaults = defaults } - static func objectForKey(defaultName: String!) -> AnyObject! { - let defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults() + subscript(key: String) -> String? { + get { + return defaults.string(forKey: key) + } - return defaults.objectForKey(defaultName) + set { + defaults.set(newValue, forKey: key) + } } +} +/*: +### Usage +*/ +let storage = Defaults() + +// Store +storage["Bishop"] = "Disconnect me. I’d rather be nothing" + +// Read +storage["Bishop"] +/*: +## 🍃 Flyweight +The flyweight pattern is used to minimize memory usage or computational expenses by sharing as much as possible with other similar objects. +### Example +*/ +// Instances of SpecialityCoffee will be the Flyweights +struct SpecialityCoffee { + let origin: String +} +protocol CoffeeSearching { + func search(origin: String) -> SpecialityCoffee? +} + +// Menu acts as a factory and cache for SpecialityCoffee flyweight objects +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)") + } + } } /*: ### Usage */ -Eternal.setObject("Disconnect me. I’d rather be nothing", forKey:"Bishop") -Eternal.objectForKey("Bishop") +let coffeeShop = CoffeeShop(menu: Menu()) + +coffeeShop.takeOrder(origin: "Yirgacheffe, Ethiopia", table: 1) +coffeeShop.takeOrder(origin: "Buziraguhindwa, Burundi", table: 3) + +coffeeShop.serve() /*: ☔ Protection Proxy ------------------ @@ -276,23 +326,23 @@ Protection proxy is restricting access. ### Example */ -protocol DoorOperator { - func openDoors(doors: String) -> String +protocol DoorOpening { + func open(doors: String) -> String } -class HAL9000 : DoorOperator { - func openDoors(doors: String) -> String { +final class HAL9000: DoorOpening { + func open(doors: String) -> String { return ("HAL9000: Affirmative, Dave. I read you. Opened \(doors).") } } -class CurrentComputer : DoorOperator { +final class CurrentComputer: DoorOpening { private var computer: HAL9000! - func authenticateWithPassword(pass: String) -> Bool { + func authenticate(password: String) -> Bool { - guard pass == "pass" else { - return false; + guard password == "pass" else { + return false } computer = HAL9000() @@ -300,30 +350,30 @@ class CurrentComputer : DoorOperator { return true } - func openDoors(doors: String) -> String { + func open(doors: String) -> String { guard computer != nil else { return "Access Denied. I'm afraid I can't do that." } - return computer.openDoors(doors) + return computer.open(doors: doors) } } /*: ### Usage */ let computer = CurrentComputer() -let doors = "Pod Bay Doors" +let podBay = "Pod Bay Doors" -computer.openDoors(doors) +computer.open(doors: podBay) -computer.authenticateWithPassword("pass") -computer.openDoors(doors) +computer.authenticate(password: "pass") +computer.open(doors: podBay) /*: 🍬 Virtual Proxy ---------------- -The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. +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. ### Example @@ -332,13 +382,14 @@ protocol HEVSuitMedicalAid { func administerMorphine() -> String } -class HEVSuit : HEVSuitMedicalAid { +final class HEVSuit: HEVSuitMedicalAid { func administerMorphine() -> String { - return "Morphine aministered." + return "Morphine administered." } } -class HEVSuitHumanInterface : HEVSuitMedicalAid { +final class HEVSuitHumanInterface: HEVSuitMedicalAid { + lazy private var physicalSuit: HEVSuit = HEVSuit() func administerMorphine() -> String { diff --git a/Design-Patterns.playground/contents.xcplayground b/Design-Patterns.playground/contents.xcplayground index 948fe0e..9c4add8 100644 --- a/Design-Patterns.playground/contents.xcplayground +++ b/Design-Patterns.playground/contents.xcplayground @@ -1,6 +1,7 @@ + diff --git a/Design-Patterns.playground/playground.xcworkspace/contents.xcworkspacedata b/Design-Patterns.playground/playground.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/Design-Patterns.playground/playground.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Design-Patterns.playground/playground.xcworkspace/xcshareddata/Design-Patterns.xccheckout b/Design-Patterns.playground/playground.xcworkspace/xcshareddata/Design-Patterns.xccheckout deleted file mode 100644 index 7ff2e0e..0000000 --- a/Design-Patterns.playground/playground.xcworkspace/xcshareddata/Design-Patterns.xccheckout +++ /dev/null @@ -1,41 +0,0 @@ - - - - - IDESourceControlProjectFavoriteDictionaryKey - - IDESourceControlProjectIdentifier - 6E6A675F-9997-43AE-BB1C-8E4B69F9AD36 - IDESourceControlProjectName - Design-Patterns - IDESourceControlProjectOriginsDictionary - - C96876E4909DB6FA99FAEC5AD77808104236AA02 - https://github.com/ochococo/Design-Patterns-In-Swift.git - - IDESourceControlProjectPath - Design-Patterns.playground - IDESourceControlProjectRelativeInstallPathDictionary - - C96876E4909DB6FA99FAEC5AD77808104236AA02 - .. - - IDESourceControlProjectURL - https://github.com/ochococo/Design-Patterns-In-Swift.git - IDESourceControlProjectVersion - 111 - IDESourceControlProjectWCCIdentifier - C96876E4909DB6FA99FAEC5AD77808104236AA02 - IDESourceControlProjectWCConfigurations - - - IDESourceControlRepositoryExtensionIdentifierKey - public.vcs.git - IDESourceControlWCCIdentifierKey - C96876E4909DB6FA99FAEC5AD77808104236AA02 - IDESourceControlWCCName - Design-Patterns-In-Swift - - - - diff --git a/GENERATE.md b/GENERATE.md deleted file mode 100644 index fafa908..0000000 --- a/GENERATE.md +++ /dev/null @@ -1,12 +0,0 @@ -How to generate playground and zip -================================== - -In Terminal type: - -every time: - -```bash -./generate-playground.sh -``` - -👍 \ No newline at end of file diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..04d6063 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +- [ ] Read [CONTRIBUTING.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/CONTRIBUTING.md]) +- [ ] Only edited files inside `source` folder (IMPORTANT) and commited them with a meaningful message +- [ ] Ran `generate-playground.sh`, no errors +- [ ] Opened playground, it worked fine +- [ ] Did not commit the changes caused by `generate-playground.sh` +- [ ] Linked to and/or created issue +- [ ] Added a description to PR diff --git a/README-CN.md b/README-CN.md new file mode 100644 index 0000000..2a3206c --- /dev/null +++ b/README-CN.md @@ -0,0 +1,1559 @@ + + +设计模式(Swift 5.0 实现) +====================== + +([Design-Patterns-CN.playground.zip](https://raw.githubusercontent.com/ochococo/Design-Patterns-In-Swift/master/Design-Patterns-CN.playground.zip)). + +👷 源项目由 [@nsmeme](http://twitter.com/nsmeme) (Oktawian Chojnacki) 维护。 + +🇨🇳 中文版由 [@binglogo](https://twitter.com/binglogo) 整理翻译。 + +🚀 如何由源代码,并生成 README 与 Playground 产物,请查看: +- [CONTRIBUTING.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/CONTRIBUTING.md) +- [CONTRIBUTING-CN.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/CONTRIBUTING-CN.md) + + + +```swift +print("您好!") +``` + + +## 目录 + +| [行为型模式](#行为型模式) | [创建型模式](#创建型模式) | [结构型模式](#结构型模式structural) | +| ------------------------------------------------------------ | --------------------------------------------------------- | ------------------------------------------------------------ | +| [🐝 责任链 Chain Of Responsibility](#-责任链chain-of-responsibility) | [🌰 抽象工厂 Abstract Factory](#-抽象工厂abstract-factory) | [🔌 适配器 Adapter](#-适配器adapter) | +| [👫 命令 Command](#-命令command) | [👷 生成器 Builder](#-生成器builder) | [🌉 桥接 Bridge](#-桥接bridge) | +| [🎶 解释器 Interpreter](#-解释器interpreter) | [🏭 工厂方法 Factory Method](#-工厂方法factory-method) | [🌿 组合 Composite](#-组合composite) | +| [🍫 迭代器 Iterator](#-迭代器iterator) | [🔂 单态 Monostate](#-单态monostate) | [🍧 修饰 Decorator](#-修饰decorator) | +| [💐 中介者 Mediator](#-中介者mediator) | [🃏 原型 Prototype](#-原型prototype) | [🎁 外观 Façade](#-外观facade) | +| [💾 备忘录 Memento](#-备忘录memento) | [💍 单例 Singleton](#-单例singleton) | [🍃 享元 Flyweight](#-享元flyweight) | +| [👓 观察者 Observer](#-观察者observer) | | [☔ 保护代理 Protection Proxy](#-保护代理模式protection-proxy) | +| [🐉 状态 State](#-状态state) | | [🍬 虚拟代理 Virtual Proxy](#-虚拟代理virtual-proxy) | +| [💡 策略 Strategy](#-策略strategy) | | | +| [📝 模板方法 Templdate Method](#-template-method) | | | +| [🏃 访问者 Visitor](#-访问者visitor) | | | + + 行为型模式 + ======== + + >在软件工程中, 行为型模式为设计模式的一种类型,用来识别对象之间的常用交流模式并加以实现。如此,可在进行这些交流活动时增强弹性。 + > + >**来源:** [维基百科](https://zh.wikipedia.org/wiki/%E8%A1%8C%E7%82%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F) + + + + +🐝 责任链(Chain Of Responsibility) +------------------------------ + +责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。 + +### 示例: + +```swift + +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 { + 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) + } +} +``` + + ### 用法 + +```swift +// 创建一系列的钱堆,并将其链接起来:10<20<50<100 +let ten = MoneyPile(value: 10, quantity: 6, next: nil) +let twenty = MoneyPile(value: 20, quantity: 2, next: ten) +let fifty = MoneyPile(value: 50, quantity: 2, next: twenty) +let hundred = MoneyPile(value: 100, quantity: 1, next: fifty) + +// 创建 ATM 实例 +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 - 1x100 +``` + +👫 命令(Command) + ------------ + 命令模式是一种设计模式,它尝试以对象来代表实际行动。命令对象可以把行动(action) 及其参数封装起来,于是这些行动可以被: + * 重复多次 + * 取消(如果该对象有实现的话) + * 取消后又再重做 + ### 示例: + +```swift +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() + } +} +``` + +### 用法 + +```swift +let podBayDoors = "Pod Bay Doors" +let doorModule = HAL9000DoorsOperations(doors:podBayDoors) + +doorModule.open() +doorModule.close() +``` + +🎶 解释器(Interpreter) + ------------------ + + 给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。 + + ### 示例: + +```swift + +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) + } +} +``` + +### 用法 + +```swift +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) +``` + +🍫 迭代器(Iterator) + --------------- + + 迭代器模式可以让用户通过特定的接口巡访容器中的每一个元素而不用了解底层的实现。 + + ### 示例: + +```swift +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) + } +} +``` + +### 用法 + +```swift +let greatNovellas = Novellas(novellas: [Novella(name: "The Mist")] ) + +for novella in greatNovellas { + print("I've read: \(novella)") +} +``` + +💐 中介者(Mediator) + --------------- + + 用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互。 + + ### 示例: + +```swift +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) + } + } +} + +``` + +### 用法 + +```swift +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) + +``` + +💾 备忘录(Memento) +-------------- + +在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态 + +### 示例: + +```swift +typealias Memento = [String: String] +``` + +发起人(Originator) + +```swift +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 ] + } +} +``` + +管理者(Caretaker) + +```swift +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) + } +} +``` + +### 用法 + +```swift +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) +} +``` + +👓 观察者(Observer) +--------------- + +一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知 + +### 示例: + +```swift +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.") + } + } +} +``` + +### 用法 + +```swift +var observerInstance = Observer() +var testChambers = TestChambers() +testChambers.observer = observerInstance +testChambers.testChamberNumber += 1 +``` + +🐉 状态(State) +--------- + +在状态模式中,对象的行为是基于它的内部状态而改变的。 +这个模式允许某个类对象在运行时发生改变。 + +### 示例: + +```swift +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 } +} +``` + +### 用法 + +```swift +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) +``` + +💡 策略(Strategy) +-------------- + +对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。策略模式: +* 定义了一族算法(业务规则); +* 封装了每个算法; +* 这族的算法可互换代替(interchangeable)。 + +### 示例: + +```swift + +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) + } +} + +``` + + ### 用法 + +```swift + +let rachel = TestSubject(pupilDiameter: 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) +``` + +📝 模板方法模式 +----------- + + 模板方法模式是一种行为设计模式, 它通过父类/协议中定义了一个算法的框架, 允许子类/具体实现对象在不修改结构的情况下重写算法的特定步骤。 + +### 示例: + +```swift +protocol Garden { + func prepareSoil() + func plantSeeds() + func waterPlants() + func prepareGarden() +} + +extension Garden { + + func prepareGarden() { + prepareSoil() + plantSeeds() + waterPlants() + } +} + +final class RoseGarden: Garden { + + func prepare() { + prepareGarden() + } + + func prepareSoil() { + print ("prepare soil for rose garden") + } + + func plantSeeds() { + print ("plant seeds for rose garden") + } + + func waterPlants() { + print ("water the rose garden") + } +} + +``` + +### 用法 + +```swift + +let roseGarden = RoseGarden() +roseGarden.prepare() +``` + +🏃 访问者(Visitor) +-------------- + +封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 + +### 示例: + +```swift +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" } +} + +``` + +### 用法 + +```swift +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 +} + +names +``` + + + 创建型模式 + ======== + + > 创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。 + > + >**来源:** [维基百科](https://zh.wikipedia.org/wiki/%E5%89%B5%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F) + + + + + +🌰 抽象工厂(Abstract Factory) +------------- + +抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。在正常使用中,客户端程序需要创建抽象工厂的具体实现,然后使用抽象工厂作为接口来创建这一主题的具体对象。 + +### 示例: + +协议 + +```swift + +protocol BurgerDescribing { + var ingredients: [String] { get } +} + +struct CheeseBurger: BurgerDescribing { + let ingredients: [String] +} + +protocol BurgerMaking { + func make() -> BurgerDescribing +} + +// 工厂方法实现 + +final class BigKahunaBurger: 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"]) + } +} + +``` + +抽象工厂 + +```swift + +enum BurgerFactoryType: BurgerMaking { + + case bigKahuna + case jackInTheBox + + func make() -> BurgerDescribing { + switch self { + case .bigKahuna: + return BigKahunaBurger().make() + case .jackInTheBox: + return JackInTheBox().make() + } + } +} +``` + +### 用法 + +```swift +let bigKahuna = BurgerFactoryType.bigKahuna.make() +let jackInTheBox = BurgerFactoryType.jackInTheBox.make() +``` + +👷 生成器(Builder) +-------------- + +一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。 + +### 示例: + +```swift +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))" + } +} +``` + +### 用法 + +```swift +let empire = DeathStarBuilder { builder in + builder.x = 0.1 + builder.y = 0.2 + builder.z = 0.3 +} + +let deathStar = DeathStar(builder:empire) +``` + +🏭 工厂方法(Factory Method) +----------------------- + +定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。 + +### 示例: + +```swift +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 + } + + } +} +``` + +### 用法 + +```swift +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 +``` + + 🔂 单态(Monostate) + ------------ + + 单态模式是实现单一共享的另一种方法。不同于单例模式,它通过完全不同的机制,在不限制构造方法的情况下实现单一共享特性。 + 因此,在这种情况下,单态会将状态保存为静态,而不是将整个实例保存为单例。 + [单例和单态 - Robert C. Martin](http://staff.cs.utu.fi/~jounsmed/doos_06/material/SingletonAndMonostate.pdf) + +### 示例: + +```swift +class Settings { + + enum Theme { + case `default` + case old + case new + } + + private static var theme: Theme? + + var currentTheme: Theme { + get { Settings.theme ?? .default } + set(newTheme) { Settings.theme = newTheme } + } +} +``` + +### 用法: + +```swift +import SwiftUI + +// 改变主题 +let settings = Settings() // 开始使用主题 .old +settings.currentTheme = .new // 改变主题为 .new + +// 界面一 +let screenColor: Color = Settings().currentTheme == .old ? .gray : .white + +// 界面二 +let screenTitle: String = Settings().currentTheme == .old ? "Itunes Connect" : "App Store Connect" +``` + +🃏 原型(Prototype) +-------------- + +通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。 + +### 示例: + +```swift +class MoonWorker { + + let name: String + var health: Int = 100 + + init(name: String) { + self.name = name + } + + func clone() -> MoonWorker { + return MoonWorker(name: name) + } +} +``` + +### 用法 + +```swift +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 = 0 +``` + +💍 单例(Singleton) +-------------- + +单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为 + +### 示例: + +```swift +final class ElonMusk { + + static let shared = ElonMusk() + + private init() { + // Private initialization to ensure just one instance is created. + } +} +``` + +### 用法 + +```swift +let elon = ElonMusk.shared // There is only one Elon Musk folks. +``` + + +结构型模式(Structural) +==================== + +> 在软件工程中结构型模式是设计模式,借由一以贯之的方式来了解元件间的关系,以简化设计。 +> +>**来源:** [维基百科](https://zh.wikipedia.org/wiki/%E7%B5%90%E6%A7%8B%E5%9E%8B%E6%A8%A1%E5%BC%8F) + + + + +🔌 适配器(Adapter) +-------------- + +适配器模式有时候也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。 + +### 示例: + +```swift +protocol NewDeathStarSuperLaserAiming { + var angleV: Double { get } + var angleH: Double { get } +} +``` + +**被适配者** + +```swift +struct OldDeathStarSuperlaserTarget { + let angleHorizontal: Float + let angleVertical: Float + + init(angleHorizontal: Float, angleVertical: Float) { + self.angleHorizontal = angleHorizontal + self.angleVertical = angleVertical + } +} +``` + +**适配器** + +```swift +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 + } +} +``` + +### 用法 + +```swift +let target = OldDeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) +let newFormat = NewDeathStarSuperlaserTarget(target) + +newFormat.angleH +newFormat.angleV +``` + +🌉 桥接(Bridge) +----------- + +桥接模式将抽象部分与实现部分分离,使它们都可以独立的变化。 + +### 示例: + +```swift +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") + } +} +``` + +### 用法 + +```swift +let tvRemoteControl = RemoteControl(appliance: TV()) +tvRemoteControl.turnOn() + +let fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner()) +fancyVacuumCleanerRemoteControl.turnOn() +``` + +🌿 组合(Composite) +-------------- + +将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 + +### 示例: + +组件(Component) + +```swift +protocol Shape { + func draw(fillColor: String) +} +``` + +叶子节点(Leafs) + +```swift +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)") + } +} + +``` + +组合 + +```swift +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) + } + } +} +``` + +### 用法 + +```swift +var whiteboard = Whiteboard(Circle(), Square()) +whiteboard.draw(fillColor: "Red") +``` + +🍧 修饰(Decorator) +-------------- + +修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。 +就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。 + +### 示例: + +```swift +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 ingredients: [String] { + return beverage.ingredients + ["Milk"] + } +} + +struct WhipCoffee: BeverageHaving { + + let beverage: BeverageDataHaving + + var cost: Double { + return beverage.cost + 0.5 + } + + var ingredients: [String] { + return beverage.ingredients + ["Whip"] + } +} +``` + +### 用法 + +```swift +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)") +``` + +🎁 外观(Facade) +----------- + +外观模式为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。 + +### 示例: + +```swift +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) + } + } +} +``` + +### 用法 + +```swift +let storage = Defaults() + +// Store +storage["Bishop"] = "Disconnect me. I’d rather be nothing" + +// Read +storage["Bishop"] +``` + +🍃 享元(Flyweight) +-------------- + +使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。 + +### 示例: + +```swift +// 特指咖啡生成的对象会是享元 +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)") + } + } +} +``` + +### 用法 + +```swift +let coffeeShop = CoffeeShop(menu: Menu()) + +coffeeShop.takeOrder(origin: "Yirgacheffe, Ethiopia", table: 1) +coffeeShop.takeOrder(origin: "Buziraguhindwa, Burundi", table: 3) + +coffeeShop.serve() +``` + +☔ 保护代理模式(Protection Proxy) +------------------ + +在代理模式中,创建一个类代表另一个底层类的功能。 +保护代理用于限制访问。 + +### 示例: + +```swift +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) + } +} +``` + +### 用法 + +```swift +let computer = CurrentComputer() +let podBay = "Pod Bay Doors" + +computer.open(doors: podBay) + +computer.authenticate(password: "pass") +computer.open(doors: podBay) +``` + +🍬 虚拟代理(Virtual Proxy) +---------------- + +在代理模式中,创建一个类代表另一个底层类的功能。 +虚拟代理用于对象的需时加载。 + +### 示例: + +```swift +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() + } +} +``` + +### 用法 + +```swift +let humanInterface = HEVSuitHumanInterface() +humanInterface.administerMorphine() +``` + + +Info +==== + +📖 Descriptions from: [Gang of Four Design Patterns Reference Sheet](http://www.blackwasp.co.uk/GangOfFour.aspx) diff --git a/README.md b/README.md index b68bb92..09f58c2 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,39 @@ -Design Patterns implemented in Swift 2.2 + +Design Patterns implemented in Swift 5.0 ======================================== -A short cheat-sheet with Xcode 7.3 Playground ([Design-Patterns.playground.zip](https://raw.githubusercontent.com/ochococo/Design-Patterns-In-Swift/master/Design-Patterns.playground.zip)). -👷 Project maintained by: [@nsmeme](http://twitter.com/nsmeme) (Oktawian Chojnacki) +A short cheat-sheet with Xcode 10.2 Playground ([Design-Patterns.playground.zip](https://raw.githubusercontent.com/ochococo/Design-Patterns-In-Swift/master/Design-Patterns.playground.zip)). + +### [🇨🇳中文版](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/README-CN.md) + +👷 Project started by: [@nsmeme](http://twitter.com/nsmeme) (Oktawian Chojnacki) + +👷 中文版由 [@binglogo](https://twitter.com/binglogo) (棒棒彬) 整理翻译。 + +🚀 How to generate README, Playground and zip from source: [CONTRIBUTING.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/CONTRIBUTING.md) + + +```swift +print("Welcome!") +``` -⚠️ See my newest project: [OOD-Principles-In-Swift](https://github.com/ochococo/OOD-Principles-In-Swift) ## Table of Contents -* [Behavioral](#behavioral) -* [Creational](#creational) -* [Structural](#structural) +| [Behavioral](#behavioral) | [Creational](#creational) | [Structural](#structural) | +| ------------------------------------------------------ | ---------------------------------------- | ---------------------------------------- | +| [🐝 Chain Of Responsibility](#-chain-of-responsibility) | [🌰 Abstract Factory](#-abstract-factory) | [🔌 Adapter](#-adapter) | +| [👫 Command](#-command) | [👷 Builder](#-builder) | [🌉 Bridge](#-bridge) | +| [🎶 Interpreter](#-interpreter) | [🏭 Factory Method](#-factory-method) | [🌿 Composite](#-composite) | +| [🍫 Iterator](#-iterator) | [🔂 Monostate](#-monostate) | [🍧 Decorator](#-decorator) | +| [💐 Mediator](#-mediator) | [🃏 Prototype](#-prototype) | [🎁 Façade](#-fa-ade) | +| [💾 Memento](#-memento) | [💍 Singleton](#-singleton) | [🍃 Flyweight](#-flyweight) | +| [👓 Observer](#-observer) | | [☔ Protection Proxy](#-protection-proxy) | +| [🐉 State](#-state) | | [🍬 Virtual Proxy](#-virtual-proxy) | +| [💡 Strategy](#-strategy) | | | +| [📝 Template Method](#-template-method) | | | +| [🏃 Visitor](#-visitor) | | | Behavioral ========== @@ -20,11 +42,8 @@ Behavioral > >**Source:** [wikipedia.org](http://en.wikipedia.org/wiki/Behavioral_pattern) -```swift -import Swift -import Foundation -``` + 🐝 Chain Of Responsibility -------------------------- @@ -35,70 +54,78 @@ The chain of responsibility pattern is used to process varied requests, each of ```swift -class MoneyPile { +protocol Withdrawing { + func withdraw(amount: Int) -> Bool +} + +final class MoneyPile: Withdrawing { + let value: Int var quantity: Int - var nextPile: MoneyPile? - - init(value: Int, quantity: Int, nextPile: MoneyPile?) { + var next: Withdrawing? + + init(value: Int, quantity: Int, next: Withdrawing?) { self.value = value self.quantity = quantity - self.nextPile = nextPile + self.next = next } - - func canWithdraw(v: Int) -> Bool { - var v = v + func withdraw(amount: Int) -> Bool { + + var amount = amount func canTakeSomeBill(want: Int) -> Bool { return (want / self.value) > 0 } - - var q = self.quantity - while canTakeSomeBill(v) { + var quantity = self.quantity + + while canTakeSomeBill(want: amount) { - if q == 0 { + if quantity == 0 { break } - v -= self.value - q -= 1 + amount -= self.value + quantity -= 1 } - if v == 0 { + guard amount > 0 else { return true - } else if let next = self.nextPile { - return next.canWithdraw(v) + } + + if let next { + return next.withdraw(amount: amount) } return false } } -class ATM { - private var hundred: MoneyPile - private var fifty: MoneyPile - private var twenty: MoneyPile - private var ten: MoneyPile - - private var startPile: MoneyPile { +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: MoneyPile, - fifty: MoneyPile, - twenty: MoneyPile, - ten: MoneyPile) { + + init(hundred: Withdrawing, + fifty: Withdrawing, + twenty: Withdrawing, + ten: Withdrawing) { self.hundred = hundred self.fifty = fifty self.twenty = twenty self.ten = ten } - - func canWithdraw(value: Int) -> String { - return "Can withdraw: \(self.startPile.canWithdraw(value))" + + func withdraw(amount: Int) -> Bool { + return startPile.withdraw(amount: amount) } } ``` @@ -106,24 +133,18 @@ class ATM { ### Usage ```swift - // 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) +let ten = MoneyPile(value: 10, quantity: 6, next: nil) +let twenty = MoneyPile(value: 20, quantity: 2, next: ten) +let fifty = MoneyPile(value: 50, quantity: 2, next: twenty) +let hundred = MoneyPile(value: 100, quantity: 1, next: fifty) // Build ATM. var atm = ATM(hundred: hundred, fifty: fifty, twenty: twenty, ten: ten) -atm.canWithdraw(310) // Cannot because ATM has only 300 -atm.canWithdraw(100) // Can withdraw - 1x100 -atm.canWithdraw(165) // Cannot withdraw because ATM doesn't has bill with value of 5 -atm.canWithdraw(30) // Can withdraw - 1x20, 2x10 +atm.withdraw(amount: 310) // Cannot because ATM has only 300 +atm.withdraw(amount: 100) // Can withdraw - 1x100 ``` ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Chain-Of-Responsibility) - - 👫 Command ---------- @@ -132,12 +153,11 @@ The command pattern is used to express a request, including the call to be made ### Example: ```swift - protocol DoorCommand { func execute() -> String } -class OpenCommand : DoorCommand { +final class OpenCommand: DoorCommand { let doors:String required init(doors: String) { @@ -149,7 +169,7 @@ class OpenCommand : DoorCommand { } } -class CloseCommand : DoorCommand { +final class CloseCommand: DoorCommand { let doors:String required init(doors: String) { @@ -161,7 +181,7 @@ class CloseCommand : DoorCommand { } } -class HAL9000DoorsOperations { +final class HAL9000DoorsOperations { let openCommand: DoorCommand let closeCommand: DoorCommand @@ -183,7 +203,6 @@ class HAL9000DoorsOperations { ### Usage: ```swift - let podBayDoors = "Pod Bay Doors" let doorModule = HAL9000DoorsOperations(doors:podBayDoors) @@ -200,69 +219,68 @@ The interpreter pattern is used to evaluate sentences in a language. ```swift - -protocol IntegerExp { - func evaluate(context: IntegerContext) -> Int - func replace(character: Character, integerExp: IntegerExp) -> IntegerExp - func copy() -> IntegerExp +protocol IntegerExpression { + func evaluate(_ context: IntegerContext) -> Int + func replace(character: Character, integerExpression: IntegerExpression) -> IntegerExpression + func copied() -> IntegerExpression } -class IntegerContext { +final class IntegerContext { private var data: [Character:Int] = [:] - + func lookup(name: Character) -> Int { return self.data[name]! } - - func assign(integerVarExp: IntegerVarExp, value: Int) { - self.data[integerVarExp.name] = value + + func assign(expression: IntegerVariableExpression, value: Int) { + self.data[expression.name] = value } } -class IntegerVarExp: IntegerExp { +final class IntegerVariableExpression: IntegerExpression { let name: Character - + init(name: Character) { self.name = name } - - func evaluate(context: IntegerContext) -> Int { - return context.lookup(self.name) + + func evaluate(_ context: IntegerContext) -> Int { + return context.lookup(name: self.name) } - - func replace(name: Character, integerExp: IntegerExp) -> IntegerExp { + + func replace(character name: Character, integerExpression: IntegerExpression) -> IntegerExpression { if name == self.name { - return integerExp.copy() + return integerExpression.copied() } else { - return IntegerVarExp(name: self.name) + return IntegerVariableExpression(name: self.name) } } - - func copy() -> IntegerExp { - return IntegerVarExp(name: self.name) + + func copied() -> IntegerExpression { + return IntegerVariableExpression(name: self.name) } } -class AddExp: IntegerExp { - private var operand1: IntegerExp - private var operand2: IntegerExp - - init(op1: IntegerExp, op2: IntegerExp) { +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 { + + func evaluate(_ context: IntegerContext) -> Int { return self.operand1.evaluate(context) + self.operand2.evaluate(context) } - - func replace(character: Character, integerExp: IntegerExp) -> IntegerExp { - return AddExp(op1: operand1.replace(character, integerExp: integerExp), - op2: operand2.replace(character, integerExp: integerExp)) + + func replace(character: Character, integerExpression: IntegerExpression) -> IntegerExpression { + return AddExpression(op1: operand1.replace(character: character, integerExpression: integerExpression), + op2: operand2.replace(character: character, integerExpression: integerExpression)) } - - func copy() -> IntegerExp { - return AddExp(op1: self.operand1, op2: self.operand2) + + func copied() -> IntegerExpression { + return AddExpression(op1: self.operand1, op2: self.operand2) } } ``` @@ -270,26 +288,21 @@ class AddExp: IntegerExp { ### Usage ```swift +var context = IntegerContext() -var expression: IntegerExp? -var intContext = IntegerContext() - -var a = IntegerVarExp(name: "A") -var b = IntegerVarExp(name: "B") -var c = IntegerVarExp(name: "C") +var a = IntegerVariableExpression(name: "A") +var b = IntegerVariableExpression(name: "B") +var c = IntegerVariableExpression(name: "C") -expression = AddExp(op1: a, op2: AddExp(op1: b, op2: c)) // a + (b + c) +var expression = AddExpression(op1: a, op2: AddExpression(op1: b, op2: c)) // a + (b + c) -intContext.assign(a, value: 2) -intContext.assign(b, value: 1) -intContext.assign(c, value: 3) +context.assign(expression: a, value: 2) +context.assign(expression: b, value: 1) +context.assign(expression: c, value: 3) -var result = expression?.evaluate(intContext) +var result = expression.evaluate(context) ``` ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Interpreter) - - 🍫 Iterator ----------- @@ -298,17 +311,32 @@ The iterator pattern is used to provide a standard interface for traversing a co ### Example: ```swift +struct Novella { + let name: String +} -struct NovellasCollection { - let novellas: [T] +struct Novellas { + let novellas: [Novella] } -extension NovellasCollection: SequenceType { - typealias Generator = AnyGenerator - - func generate() -> AnyGenerator { - var i = 0 - return AnyGenerator { i += 1; return i >= self.novellas.count ? nil : self.novellas[i] } +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) } } ``` @@ -316,8 +344,7 @@ extension NovellasCollection: SequenceType { ### Usage ```swift - -let greatNovellas = NovellasCollection(novellas:["Mist"]) +let greatNovellas = Novellas(novellas: [Novella(name: "The Mist")] ) for novella in greatNovellas { print("I've read: \(novella)") @@ -332,70 +359,65 @@ The mediator pattern is used to reduce coupling between classes that communicate ### Example ```swift +protocol Receiver { + associatedtype MessageType + func receive(message: MessageType) +} +protocol Sender { + associatedtype MessageType + associatedtype ReceiverType: Receiver + + var recipients: [ReceiverType] { get } + + func send(message: MessageType) +} -class Colleague { +struct Programmer: Receiver { let name: String - let mediator: Mediator - init(name: String, mediator: Mediator) { + init(name: String) { self.name = name - self.mediator = mediator - } - - func send(message: String) { - mediator.send(message, colleague: self) } func receive(message: String) { - assert(false, "Method should be overriden") + print("\(name) received: \(message)") } } -protocol Mediator { - func send(message: String, colleague: Colleague) -} - -class MessageMediator: Mediator { - private var colleagues: [Colleague] = [] +final class MessageMediator: Sender { + internal var recipients: [Programmer] = [] - func addColleague(colleague: Colleague) { - colleagues.append(colleague) + func add(recipient: Programmer) { + recipients.append(recipient) } - func send(message: String, colleague: Colleague) { - for c in colleagues { - if c !== colleague { //for simplicity we compare object references - c.receive(message) - } + func send(message: String) { + for recipient in recipients { + recipient.receive(message: message) } } } -class ConcreteColleague: Colleague { - override func receive(message: String) { - print("Colleague \(name) received: \(message)") - } -} - ``` ### Usage ```swift - +func spamMonster(message: String, worker: MessageMediator) { + worker.send(message: message) +} let messagesMediator = MessageMediator() -let user0 = ConcreteColleague(name: "0", mediator: messagesMediator) -let user1 = ConcreteColleague(name: "1", mediator: messagesMediator) -messagesMediator.addColleague(user0) -messagesMediator.addColleague(user1) -user0.send("Hello") // user1 receives message -``` +let user0 = Programmer(name: "Linus Torvalds") +let user1 = Programmer(name: "Avadis 'Avie' Tevanian") +messagesMediator.add(recipient: user0) +messagesMediator.add(recipient: user1) ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Mediator) +spamMonster(message: "I'd Like to Add you to My Professional Network", worker: messagesMediator) +``` 💾 Memento ---------- @@ -405,29 +427,44 @@ The memento pattern is used to capture the current state of an object and store ### Example ```swift - -typealias Memento = Dictionary - -let DPMementoKeyChapter = "com.valve.halflife.chapter" -let DPMementoKeyWeapon = "com.valve.halflife.weapon" -let DPMementoGameState = "com.valve.halflife.state" +typealias Memento = [String: String] ``` Originator ```swift +protocol MementoConvertible { + var memento: Memento { get } + init?(memento: Memento) +} -class GameState { - var chapter: String = "" - var weapon: String = "" +struct GameState: MementoConvertible { - func toMemento() -> Memento { - return [ DPMementoKeyChapter:chapter, DPMementoKeyWeapon:weapon ] + private enum Keys { + static let chapter = "com.valve.halflife.chapter" + static let weapon = "com.valve.halflife.weapon" } - func restoreFromMemento(memento: Memento) { - chapter = memento[DPMementoKeyChapter] as? String ?? "n/a" - weapon = memento[DPMementoKeyWeapon] as? String ?? "n/a" + 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 ] } } ``` @@ -435,86 +472,82 @@ class GameState { Caretaker ```swift - enum CheckPoint { - static func saveState(memento: Memento, keyName: String = DPMementoGameState) { - let defaults = NSUserDefaults.standardUserDefaults() - defaults.setObject(memento, forKey: keyName) + + private static let defaults = UserDefaults.standard + + static func save(_ state: MementoConvertible, saveName: String) { + defaults.set(state.memento, forKey: saveName) defaults.synchronize() } - static func restorePreviousState(keyName keyName: String = DPMementoGameState) -> Memento { - let defaults = NSUserDefaults.standardUserDefaults() - - return defaults.objectForKey(keyName) as? Memento ?? Memento() + static func restore(saveName: String) -> Any? { + return defaults.object(forKey: saveName) } } ``` - ### Usage +### Usage ```swift - -var gameState = GameState() -gameState.restoreFromMemento(CheckPoint.restorePreviousState()) - -gameState.chapter = "Black Mesa Inbound" -gameState.weapon = "Crowbar" -CheckPoint.saveState(gameState.toMemento()) +var gameState = GameState(chapter: "Black Mesa Inbound", weapon: "Crowbar") gameState.chapter = "Anomalous Materials" gameState.weapon = "Glock 17" -gameState.restoreFromMemento(CheckPoint.restorePreviousState()) +CheckPoint.save(gameState, saveName: "gameState1") gameState.chapter = "Unforeseen Consequences" gameState.weapon = "MP5" -CheckPoint.saveState(gameState.toMemento(), keyName: "gameState2") +CheckPoint.save(gameState, saveName: "gameState2") gameState.chapter = "Office Complex" gameState.weapon = "Crossbow" -CheckPoint.saveState(gameState.toMemento()) - -gameState.restoreFromMemento(CheckPoint.restorePreviousState(keyName: "gameState2")) +CheckPoint.save(gameState, saveName: "gameState3") +if let memento = CheckPoint.restore(saveName: "gameState1") as? Memento { + let finalState = GameState(memento: memento) + dump(finalState) +} ``` 👓 Observer ----------- -The observer pattern is used to allow an object to publish changes to its state. +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. ### Example ```swift - protocol PropertyObserver : class { - func willChangePropertyName(propertyName:String, newPropertyValue:AnyObject?) - func didChangePropertyName(propertyName:String, oldPropertyValue:AnyObject?) + func willChange(propertyName: String, newPropertyValue: Any?) + func didChange(propertyName: String, oldPropertyValue: Any?) } -class TestChambers { +final class TestChambers { weak var observer:PropertyObserver? + private let testChamberNumberName = "testChamberNumber" + var testChamberNumber: Int = 0 { willSet(newValue) { - observer?.willChangePropertyName("testChamberNumber", newPropertyValue:newValue) + observer?.willChange(propertyName: testChamberNumberName, newPropertyValue: newValue) } didSet { - observer?.didChangePropertyName("testChamberNumber", oldPropertyValue:oldValue) + observer?.didChange(propertyName: testChamberNumberName, oldPropertyValue: oldValue) } } } -class Observer : PropertyObserver { - func willChangePropertyName(propertyName: String, newPropertyValue: AnyObject?) { +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 didChangePropertyName(propertyName: String, oldPropertyValue: AnyObject?) { + 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.") } @@ -525,45 +558,39 @@ class Observer : PropertyObserver { ### Usage ```swift - var observerInstance = Observer() var testChambers = TestChambers() testChambers.observer = observerInstance -testChambers.testChamberNumber++ +testChambers.testChamberNumber += 1 ``` ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Observer) - - 🐉 State --------- -The state pattern is used to alter the behaviour of an object as its internal state changes. +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. ### Example ```swift - -class Context { +final class Context { private var state: State = UnauthorizedState() var isAuthorized: Bool { - get { return state.isAuthorized(self) } + get { return state.isAuthorized(context: self) } } var userId: String? { - get { return state.userId(self) } + get { return state.userId(context: self) } } - func changeStateToAuthorized(userId userId: String) { + func changeStateToAuthorized(userId: String) { state = AuthorizedState(userId: userId) } func changeStateToUnauthorized() { state = UnauthorizedState() } - } protocol State { @@ -591,17 +618,14 @@ class AuthorizedState: State { ### Usage ```swift - -let context = Context() -(context.isAuthorized, context.userId) -context.changeStateToAuthorized(userId: "admin") -(context.isAuthorized, context.userId) // now logged in as "admin" -context.changeStateToUnauthorized() -(context.isAuthorized, context.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](https://github.com/kingreza/Swift-State) - 💡 Strategy ----------- @@ -611,49 +635,111 @@ The strategy pattern is used to create an interchangeable family of algorithms f ```swift -protocol PrintStrategy { - func printString(string: String) -> String +struct TestSubject { + let pupilDiameter: Double + let blushResponse: Double + let isOrganic: Bool } -class Printer { +protocol RealnessTesting: AnyObject { + func testRealness(_ testSubject: TestSubject) -> Bool +} - let strategy: PrintStrategy - - func printString(string: String) -> String { - return self.strategy.printString(string) - } - - init(strategy: PrintStrategy) { - self.strategy = strategy +final class VoightKampffTest: RealnessTesting { + func testRealness(_ testSubject: TestSubject) -> Bool { + return testSubject.pupilDiameter < 30.0 || testSubject.blushResponse == 0.0 } } -class UpperCaseStrategy : PrintStrategy { - func printString(string:String) -> String { - return string.uppercaseString +final class GeneticTest: RealnessTesting { + func testRealness(_ testSubject: TestSubject) -> Bool { + return testSubject.isOrganic } } -class LowerCaseStrategy : PrintStrategy { - func printString(string:String) -> String { - return string.lowercaseString +final class BladeRunner { + private let strategy: RealnessTesting + + init(test: RealnessTesting) { + self.strategy = test + } + + func testIfAndroid(_ testSubject: TestSubject) -> Bool { + return !strategy.testRealness(testSubject) } } + ``` -### Usage + ### Usage + +```swift + +let rachel = TestSubject(pupilDiameter: 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) +``` + +📝 Template Method +----------- + + The template method pattern defines the steps of an algorithm and allows the redefinition of one or more of these steps. In this way, the template method protects the algorithm, the order of execution and provides abstract methods that can be implemented by concrete types. + +### Example ```swift +protocol Garden { + func prepareSoil() + func plantSeeds() + func waterPlants() + func prepareGarden() +} -var lower = Printer(strategy:LowerCaseStrategy()) -lower.printString("O tempora, o mores!") +extension Garden { + + func prepareGarden() { + prepareSoil() + plantSeeds() + waterPlants() + } +} + +final class RoseGarden: Garden { + + func prepare() { + prepareGarden() + } + + func prepareSoil() { + print ("prepare soil for rose garden") + } + + func plantSeeds() { + print ("plant seeds for rose garden") + } + + func waterPlants() { + print ("water the rose garden") + } +} -var upper = Printer(strategy:UpperCaseStrategy()) -upper.printString("O tempora, o mores!") ``` ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Strategy) +### Usage + +```swift +let roseGarden = RoseGarden() +roseGarden.prepare() +``` 🏃 Visitor ---------- @@ -663,53 +749,59 @@ The visitor pattern is used to separate a relatively complex set of structured d ### Example ```swift - protocol PlanetVisitor { func visit(planet: PlanetAlderaan) func visit(planet: PlanetCoruscant) func visit(planet: PlanetTatooine) + func visit(planet: MoonJedha) } protocol Planet { func accept(visitor: PlanetVisitor) } -class PlanetAlderaan: Planet { - func accept(visitor: PlanetVisitor) { visitor.visit(self) } +final class MoonJedha: Planet { + func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) } } -class PlanetCoruscant: Planet { - func accept(visitor: PlanetVisitor) { visitor.visit(self) } + +final class PlanetAlderaan: Planet { + func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) } } -class PlanetTatooine: Planet { - func accept(visitor: PlanetVisitor) { visitor.visit(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) } } -class NameVisitor: PlanetVisitor { +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" } } + ``` ### Usage ```swift - -let planets: [Planet] = [PlanetAlderaan(), PlanetCoruscant(), PlanetTatooine()] +let planets: [Planet] = [PlanetAlderaan(), PlanetCoruscant(), PlanetTatooine(), MoonJedha()] let names = planets.map { (planet: Planet) -> String in let visitor = NameVisitor() - planet.accept(visitor) - return visitor.name + planet.accept(visitor: visitor) + + return visitor.name } names ``` ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Visitor) - Creational ========== @@ -718,11 +810,8 @@ Creational > >**Source:** [wikipedia.org](http://en.wikipedia.org/wiki/Creational_pattern) -```swift -import Swift -import Foundation -``` + 🌰 Abstract Factory ------------------- @@ -732,59 +821,53 @@ The "family" of objects created by the factory are determined at run-time. ### Example - Protocols ```swift -protocol Decimal { - func stringValue() -> String - // factory - static func make(string : String) -> Decimal +protocol BurgerDescribing { + var ingredients: [String] { get } } -typealias NumberFactory = (String) -> Decimal +struct CheeseBurger: BurgerDescribing { + let ingredients: [String] +} -// Number implementations with factory methods +protocol BurgerMaking { + func make() -> BurgerDescribing +} -struct NextStepNumber : Decimal { - private var nextStepNumber : NSNumber +// Number implementations with factory methods - func stringValue() -> String { return nextStepNumber.stringValue } - - // factory - static func make(string : String) -> Decimal { - return NextStepNumber(nextStepNumber:NSNumber(longLong:(string as NSString).longLongValue)) +final class BigKahunaBurger: BurgerMaking { + func make() -> BurgerDescribing { + return CheeseBurger(ingredients: ["Cheese", "Burger", "Lettuce", "Tomato"]) } } -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) +final class JackInTheBox: BurgerMaking { + func make() -> BurgerDescribing { + return CheeseBurger(ingredients: ["Cheese", "Burger", "Tomato", "Onions"]) } } + ``` Abstract factory ```swift -enum NumberType { - case NextStep, Swift -} +enum BurgerFactoryType: BurgerMaking { + + case bigKahuna + case jackInTheBox -enum NumberHelper { - static func factoryFor(type : NumberType) -> NumberFactory { - switch type { - case .NextStep: - return NextStepNumber.make - case .Swift: - return SwiftNumber.make + func make() -> BurgerDescribing { + switch self { + case .bigKahuna: + return BigKahunaBurger().make() + case .jackInTheBox: + return JackInTheBox().make() } } } @@ -793,14 +876,8 @@ enum NumberHelper { ### Usage ```swift - -let factoryOne = NumberHelper.factoryFor(.NextStep) -let numberOne = factoryOne("1") -numberOne.stringValue() - -let factoryTwo = NumberHelper.factoryFor(.Swift) -let numberTwo = factoryTwo("2") -numberTwo.stringValue() +let bigKahuna = BurgerFactoryType.bigKahuna.make() +let jackInTheBox = BurgerFactoryType.jackInTheBox.make() ``` 👷 Builder @@ -812,8 +889,7 @@ An external class controls the construction algorithm. ### Example ```swift - -class DeathStarBuilder { +final class DeathStarBuilder { var x: Double? var y: Double? @@ -834,7 +910,7 @@ struct DeathStar : CustomStringConvertible { init?(builder: DeathStarBuilder) { - if let x = builder.x, y = builder.y, z = builder.z { + if let x = builder.x, let y = builder.y, let z = builder.z { self.x = x self.y = y self.z = z @@ -852,7 +928,6 @@ struct DeathStar : CustomStringConvertible { ### Usage ```swift - let empire = DeathStarBuilder { builder in builder.x = 0.1 builder.y = 0.2 @@ -862,9 +937,6 @@ let empire = DeathStarBuilder { builder in let deathStar = DeathStar(builder:empire) ``` ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Builder) - - 🏭 Factory Method ----------------- @@ -873,43 +945,45 @@ The factory pattern is used to replace class constructors, abstracting the proce ### Example ```swift - -protocol Currency { - func symbol() -> String - func code() -> String +protocol CurrencyDescribing { + var symbol: String { get } + var code: String { get } } -class Euro : Currency { - func symbol() -> String { +final class Euro: CurrencyDescribing { + var symbol: String { return "€" } - func code() -> String { + var code: String { return "EUR" } } -class UnitedStatesDolar : Currency { - func symbol() -> String { +final class UnitedStatesDolar: CurrencyDescribing { + var symbol: String { return "$" } - func code() -> String { + var code: String { return "USD" } } enum Country { - case UnitedStates, Spain, UK, Greece + case unitedStates + case spain + case uk + case greece } enum CurrencyFactory { - static func currencyForCountry(country:Country) -> Currency? { + static func currency(for country: Country) -> CurrencyDescribing? { switch country { - case .Spain, .Greece : + case .spain, .greece: return Euro() - case .UnitedStates : + case .unitedStates: return UnitedStatesDolar() default: return nil @@ -922,13 +996,56 @@ enum CurrencyFactory { ### Usage ```swift - let noCurrencyCode = "No Currency Code Available" -CurrencyFactory.currencyForCountry(.Greece)?.code() ?? noCurrencyCode -CurrencyFactory.currencyForCountry(.Spain)?.code() ?? noCurrencyCode -CurrencyFactory.currencyForCountry(.UnitedStates)?.code() ?? noCurrencyCode -CurrencyFactory.currencyForCountry(.UK)?.code() ?? noCurrencyCode +CurrencyFactory.currency(for: .greece)?.code ?? noCurrencyCode +CurrencyFactory.currency(for: .spain)?.code ?? noCurrencyCode +CurrencyFactory.currency(for: .unitedStates)?.code ?? noCurrencyCode +CurrencyFactory.currency(for: .uk)?.code ?? noCurrencyCode +``` + + 🔂 Monostate + ------------ + + The monostate pattern is another way to achieve singularity. It works through a completely different mechanism, it enforces the behavior of singularity without imposing structural constraints. + So in that case, monostate saves the state as static instead of the entire instance as a singleton. + [SINGLETON and MONOSTATE - Robert C. Martin](http://staff.cs.utu.fi/~jounsmed/doos_06/material/SingletonAndMonostate.pdf) + +### Example: + +```swift +class Settings { + + enum Theme { + case `default` + case old + case new + } + + private static var theme: Theme? + + var currentTheme: Theme { + get { Settings.theme ?? .default } + set(newTheme) { Settings.theme = newTheme } + } +} +``` + +### Usage: + +```swift + +import SwiftUI + +// When change the theme +let settings = Settings() // Starts using theme .old +settings.currentTheme = .new // Change theme to .new + +// On screen 1 +let screenColor: Color = Settings().currentTheme == .old ? .gray : .white + +// On screen 2 +let screenTitle: String = Settings().currentTheme == .old ? "Itunes Connect" : "App Store Connect" ``` 🃏 Prototype @@ -940,17 +1057,17 @@ This practise is particularly useful when the construction of a new object is in ### Example ```swift +class MoonWorker { -class ChungasRevengeDisplay { - var name: String? - let font: String + let name: String + var health: Int = 100 - init(font: String) { - self.font = font + init(name: String) { + self.name = name } - func clone() -> ChungasRevengeDisplay { - return ChungasRevengeDisplay(font:self.font) + func clone() -> MoonWorker { + return MoonWorker(name: name) } } ``` @@ -958,22 +1075,18 @@ class ChungasRevengeDisplay { ### Usage ```swift +let prototype = MoonWorker(name: "Sam Bell") -let Prototype = ChungasRevengeDisplay(font:"GotanProject") +var bell1 = prototype.clone() +bell1.health = 12 -let Philippe = Prototype.clone() -Philippe.name = "Philippe" +var bell2 = prototype.clone() +bell2.health = 23 -let Christoph = Prototype.clone() -Christoph.name = "Christoph" - -let Eduardo = Prototype.clone() -Eduardo.name = "Eduardo" +var bell3 = prototype.clone() +bell3.health = 0 ``` ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Prototype) - - 💍 Singleton ------------ @@ -984,9 +1097,9 @@ There are very few applications, do not overuse this pattern! ### Example: ```swift +final class ElonMusk { -class DeathStarSuperlaser { - static let sharedInstance = DeathStarSuperlaser() + static let shared = ElonMusk() private init() { // Private initialization to ensure just one instance is created. @@ -997,11 +1110,10 @@ class DeathStarSuperlaser { ### Usage: ```swift - -let laser = DeathStarSuperlaser.sharedInstance - +let elon = ElonMusk.shared // There is only one Elon Musk folks. ``` + Structural ========== @@ -1009,11 +1121,8 @@ Structural > >**Source:** [wikipedia.org](http://en.wikipedia.org/wiki/Structural_pattern) -```swift -import Swift -import Foundation -``` + 🔌 Adapter ---------- @@ -1023,22 +1132,20 @@ The adapter pattern is used to provide a link between two otherwise incompatible ### Example ```swift - -protocol OlderDeathStarSuperLaserAiming { - var angleV: NSNumber {get} - var angleH: NSNumber {get} +protocol NewDeathStarSuperLaserAiming { + var angleV: Double { get } + var angleH: Double { get } } ``` **Adaptee** ```swift +struct OldDeathStarSuperlaserTarget { + let angleHorizontal: Float + let angleVertical: Float -struct DeathStarSuperlaserTarget { - let angleHorizontal: Double - let angleVertical: Double - - init(angleHorizontal:Double, angleVertical:Double) { + init(angleHorizontal: Float, angleVertical: Float) { self.angleHorizontal = angleHorizontal self.angleVertical = angleVertical } @@ -1048,19 +1155,19 @@ struct DeathStarSuperlaserTarget { **Adapter** ```swift +struct NewDeathStarSuperlaserTarget: NewDeathStarSuperLaserAiming { -struct OldDeathStarSuperlaserTarget : OlderDeathStarSuperLaserAiming { - private let target : DeathStarSuperlaserTarget + private let target: OldDeathStarSuperlaserTarget - var angleV:NSNumber { - return NSNumber(double: target.angleVertical) + var angleV: Double { + return Double(target.angleVertical) } - var angleH:NSNumber { - return NSNumber(double: target.angleHorizontal) + var angleH: Double { + return Double(target.angleHorizontal) } - init(_ target:DeathStarSuperlaserTarget) { + init(_ target: OldDeathStarSuperlaserTarget) { self.target = target } } @@ -1069,17 +1176,13 @@ struct OldDeathStarSuperlaserTarget : OlderDeathStarSuperLaserAiming { ### Usage ```swift +let target = OldDeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) +let newFormat = NewDeathStarSuperlaserTarget(target) -let target = DeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) -let oldFormat = OldDeathStarSuperlaserTarget(target) - -oldFormat.angleH -oldFormat.angleV +newFormat.angleH +newFormat.angleV ``` ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Adapter) - - 🌉 Bridge ---------- @@ -1088,9 +1191,8 @@ The bridge pattern is used to separate the abstract elements of a class from the ### Example ```swift - protocol Switch { - var appliance: Appliance {get set} + var appliance: Appliance { get set } func turnOn() } @@ -1098,7 +1200,7 @@ protocol Appliance { func run() } -class RemoteControl: Switch { +final class RemoteControl: Switch { var appliance: Appliance func turnOn() { @@ -1110,13 +1212,13 @@ class RemoteControl: Switch { } } -class TV: Appliance { +final class TV: Appliance { func run() { print("tv turned on"); } } -class VacuumCleaner: Appliance { +final class VacuumCleaner: Appliance { func run() { print("vacuum cleaner turned on") } @@ -1126,11 +1228,10 @@ class VacuumCleaner: Appliance { ### Usage ```swift - -var tvRemoteControl = RemoteControl(appliance: TV()) +let tvRemoteControl = RemoteControl(appliance: TV()) tvRemoteControl.turnOn() -var fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner()) +let fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner()) fancyVacuumCleanerRemoteControl.turnOn() ``` @@ -1141,27 +1242,24 @@ The composite pattern is used to create hierarchical, recursive tree structures ### Example - Component ```swift - protocol Shape { func draw(fillColor: String) } ``` - + Leafs ```swift - -class Square : Shape { +final class Square: Shape { func draw(fillColor: String) { print("Drawing a Square with color \(fillColor)") } } -class Circle : Shape { +final class Circle: Shape { func draw(fillColor: String) { print("Drawing a circle with color \(fillColor)") } @@ -1172,17 +1270,17 @@ class Circle : Shape { Composite ```swift +final class Whiteboard: Shape { -class Whiteboard : Shape { - lazy var shapes = [Shape]() - - init(_ shapes:Shape...) { + private lazy var shapes = [Shape]() + + init(_ shapes: Shape...) { self.shapes = shapes } - - func draw(fillColor:String) { + + func draw(fillColor: String) { for shape in self.shapes { - shape.draw(fillColor) + shape.draw(fillColor: fillColor) } } } @@ -1191,9 +1289,8 @@ class Whiteboard : Shape { ### Usage: ```swift - var whiteboard = Whiteboard(Circle(), Square()) -whiteboard.draw("Red") +whiteboard.draw(fillColor: "Red") ``` 🍧 Decorator @@ -1205,64 +1302,48 @@ This provides a flexible alternative to using inheritance to modify behaviour. ### Example ```swift - -protocol Coffee { - func getCost() -> Double - func getIngredients() -> String +protocol CostHaving { + var cost: Double { get } } -class SimpleCoffee: Coffee { - func getCost() -> Double { - return 1.0 - } - - func getIngredients() -> String { - return "Coffee" - } +protocol IngredientsHaving { + var ingredients: [String] { get } } -class CoffeeDecorator: Coffee { - private let decoratedCoffee: Coffee - private let ingredientSeparator: String = ", " - - required init(decoratedCoffee: Coffee) { - self.decoratedCoffee = decoratedCoffee - } +typealias BeverageDataHaving = CostHaving & IngredientsHaving - func getCost() -> Double { - return decoratedCoffee.getCost() - } +struct SimpleCoffee: BeverageDataHaving { + let cost: Double = 1.0 + let ingredients = ["Water", "Coffee"] +} - func getIngredients() -> String { - return decoratedCoffee.getIngredients() - } +protocol BeverageHaving: BeverageDataHaving { + var beverage: BeverageDataHaving { get } } -class Milk: CoffeeDecorator { - required init(decoratedCoffee: Coffee) { - super.init(decoratedCoffee: decoratedCoffee) - } +struct Milk: BeverageHaving { - override func getCost() -> Double { - return super.getCost() + 0.5 + let beverage: BeverageDataHaving + + var cost: Double { + return beverage.cost + 0.5 } - override func getIngredients() -> String { - return super.getIngredients() + ingredientSeparator + "Milk" + var ingredients: [String] { + return beverage.ingredients + ["Milk"] } } -class WhipCoffee: CoffeeDecorator { - required init(decoratedCoffee: Coffee) { - super.init(decoratedCoffee: decoratedCoffee) - } +struct WhipCoffee: BeverageHaving { - override func getCost() -> Double { - return super.getCost() + 0.7 + let beverage: BeverageDataHaving + + var cost: Double { + return beverage.cost + 0.5 } - override func getIngredients() -> String { - return super.getIngredients() + ingredientSeparator + "Whip" + var ingredients: [String] { + return beverage.ingredients + ["Whip"] } } ``` @@ -1270,13 +1351,12 @@ class WhipCoffee: CoffeeDecorator { ### Usage: ```swift - -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())") +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)") ``` 🎁 Façade @@ -1287,30 +1367,95 @@ The facade pattern is used to define a simplified interface to a more complex su ### Example ```swift +final class Defaults { -enum Eternal { + private let defaults: UserDefaults - static func setObject(value: AnyObject!, forKey defaultName: String!) { - let defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults() - defaults.setObject(value, forKey:defaultName) - defaults.synchronize() + init(defaults: UserDefaults = .standard) { + self.defaults = defaults + } + + subscript(key: String) -> String? { + get { + return defaults.string(forKey: key) + } + + set { + defaults.set(newValue, forKey: key) + } + } +} +``` + +### Usage + +```swift +let storage = Defaults() + +// Store +storage["Bishop"] = "Disconnect me. I’d rather be nothing" + +// Read +storage["Bishop"] +``` + +## 🍃 Flyweight +The flyweight pattern is used to minimize memory usage or computational expenses by sharing as much as possible with other similar objects. +### Example + +```swift +// Instances of SpecialityCoffee will be the Flyweights +struct SpecialityCoffee { + let origin: String +} + +protocol CoffeeSearching { + func search(origin: String) -> SpecialityCoffee? +} + +// Menu acts as a factory and cache for SpecialityCoffee flyweight objects +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] } +} - static func objectForKey(defaultName: String!) -> AnyObject! { - let defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults() +final class CoffeeShop { + private var orders: [Int: SpecialityCoffee] = [:] + private let menu: CoffeeSearching + + init(menu: CoffeeSearching) { + self.menu = menu + } - return defaults.objectForKey(defaultName) + 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)") + } + } } ``` ### Usage ```swift +let coffeeShop = CoffeeShop(menu: Menu()) -Eternal.setObject("Disconnect me. I’d rather be nothing", forKey:"Bishop") -Eternal.objectForKey("Bishop") +coffeeShop.takeOrder(origin: "Yirgacheffe, Ethiopia", table: 1) +coffeeShop.takeOrder(origin: "Buziraguhindwa, Burundi", table: 3) + +coffeeShop.serve() ``` ☔ Protection Proxy @@ -1322,24 +1467,23 @@ Protection proxy is restricting access. ### Example ```swift - -protocol DoorOperator { - func openDoors(doors: String) -> String +protocol DoorOpening { + func open(doors: String) -> String } -class HAL9000 : DoorOperator { - func openDoors(doors: String) -> String { +final class HAL9000: DoorOpening { + func open(doors: String) -> String { return ("HAL9000: Affirmative, Dave. I read you. Opened \(doors).") } } -class CurrentComputer : DoorOperator { +final class CurrentComputer: DoorOpening { private var computer: HAL9000! - func authenticateWithPassword(pass: String) -> Bool { + func authenticate(password: String) -> Bool { - guard pass == "pass" else { - return false; + guard password == "pass" else { + return false } computer = HAL9000() @@ -1347,13 +1491,13 @@ class CurrentComputer : DoorOperator { return true } - func openDoors(doors: String) -> String { + func open(doors: String) -> String { guard computer != nil else { return "Access Denied. I'm afraid I can't do that." } - return computer.openDoors(doors) + return computer.open(doors: doors) } } ``` @@ -1361,37 +1505,36 @@ class CurrentComputer : DoorOperator { ### Usage ```swift - let computer = CurrentComputer() -let doors = "Pod Bay Doors" +let podBay = "Pod Bay Doors" -computer.openDoors(doors) +computer.open(doors: podBay) -computer.authenticateWithPassword("pass") -computer.openDoors(doors) +computer.authenticate(password: "pass") +computer.open(doors: podBay) ``` 🍬 Virtual Proxy ---------------- -The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. +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. ### Example ```swift - protocol HEVSuitMedicalAid { func administerMorphine() -> String } -class HEVSuit : HEVSuitMedicalAid { +final class HEVSuit: HEVSuitMedicalAid { func administerMorphine() -> String { - return "Morphine aministered." + return "Morphine administered." } } -class HEVSuitHumanInterface : HEVSuitMedicalAid { +final class HEVSuitHumanInterface: HEVSuitMedicalAid { + lazy private var physicalSuit: HEVSuit = HEVSuit() func administerMorphine() -> String { @@ -1403,7 +1546,6 @@ class HEVSuitHumanInterface : HEVSuitMedicalAid { ### Usage ```swift - let humanInterface = HEVSuitHumanInterface() humanInterface.administerMorphine() ``` @@ -1413,8 +1555,3 @@ Info ==== 📖 Descriptions from: [Gang of Four Design Patterns Reference Sheet](http://www.blackwasp.co.uk/GangOfFour.aspx) - -🚀 How to generate playground (+zip) from source: [GENERATE.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/GENERATE.md) - - -```swift diff --git a/generate-playground-cn.sh b/generate-playground-cn.sh new file mode 100755 index 0000000..933b1d0 --- /dev/null +++ b/generate-playground-cn.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +# Note: I think this part is absolute garbage but it's a snapshot of my current skills with Bash. +# Would love to rewrite it in Swift soon. + +combineSwiftCN() { + cat source-cn/startComment > $2 + cat $1/header.md >> $2 + cat source-cn/contents.md >> $2 + cat source-cn/endComment >> $2 + cat source-cn/imports.swift >> $2 + cat $1/*.swift >> $2 + { rm $2 && awk '{gsub("\\*//\\*:", "", $0); print}' > $2; } < $2 +} + +move() { + mv $1.swift Design-Patterns-CN.playground/Pages/$1.xcplaygroundpage/Contents.swift +} + +playground() { + combineSwiftCN source-cn/$1 $1.swift + move $1 +} + +combineMarkdown() { + cat $1/header.md > $2 + + { rm $2 && awk '{gsub("\\*/", "", $0); print}' > $2; } < $2 + { rm $2 && awk '{gsub("/\\*:", "", $0); print}' > $2; } < $2 + + cat source-cn/startSwiftCode >> $2 + cat $1/*.swift >> $2 + + { rm $2 && awk '{gsub("\\*//\\*:", "", $0); print}' > $2; } < $2 + { rm $2 && awk '{gsub("\\*/", "\n```swift", $0); print}' > $2; } < $2 + { rm $2 && awk '{gsub("/\\*:", "```\n", $0); print}' > $2; } < $2 + + cat source-cn/endSwiftCode >> $2 + + { rm $2 && awk '{gsub("```swift```", "", $0); print}' > $2; } < $2 + + cat $2 >> README-CN.md + rm $2 +} + +readme() { + combineMarkdown source-cn/$1 $1.md +} + +playground Index +playground Behavioral +playground Creational +playground Structural + +zip -r -X Design-Patterns-CN.playground.zip ./Design-Patterns-CN.playground + +echo "" > README-CN.md + +readme Index +cat source-cn/contentsReadme.md >> README-CN.md +readme Behavioral +readme Creational +readme Structural +cat source-cn/footer.md >> README-CN.md \ No newline at end of file diff --git a/generate-playground.sh b/generate-playground.sh index 045c657..9e87220 100755 --- a/generate-playground.sh +++ b/generate-playground.sh @@ -1,36 +1,64 @@ #!/bin/bash -rm ./contents.swift +# Note: I think this part is absolute garbage but it's a snapshot of my current skills with Bash. +# Would love to rewrite it in Swift soon. -cleanThisMessForReadme () { +combineSwift() { + cat source/startComment > $2 + cat $1/header.md >> $2 + cat source/contents.md >> $2 + cat source/endComment >> $2 + cat source/imports.swift >> $2 + cat $1/*.swift >> $2 + { rm $2 && awk '{gsub("\\*//\\*:", "", $0); print}' > $2; } < $2 +} - FILENAME=$1 +move() { + mv $1.swift Design-Patterns.playground/Pages/$1.xcplaygroundpage/Contents.swift +} - { rm $FILENAME && awk '{gsub("\\*/", "\n```swift\n", $0); print}' > $FILENAME; } < $FILENAME - { rm $FILENAME && awk '{gsub("\\*//\\*:", "", $0); print}' > $FILENAME; } < $FILENAME - { rm $FILENAME && awk '{gsub("/\\*:", "```\n", $0); print}' > $FILENAME; } < $FILENAME - { rm $FILENAME && awk '{gsub("//\\*:", "", $0); print}' > $FILENAME; } < $FILENAME - { rm $FILENAME && awk '{gsub("//:", "", $0); print}' > $FILENAME; } < $FILENAME - { rm $FILENAME && awk 'NR>1{print buf}{buf = $0}' > $FILENAME; } < $FILENAME +playground() { + combineSwift source/$1 $1.swift + move $1 } -cat source/behavioral/* > ./Behavioral.swift -cat source/creational/* > ./Creational.swift -cat source/structural/* > ./Structural.swift +combineMarkdown() { + cat $1/header.md > $2 + + { rm $2 && awk '{gsub("\\*/", "", $0); print}' > $2; } < $2 + { rm $2 && awk '{gsub("/\\*:", "", $0); print}' > $2; } < $2 + + cat source/startSwiftCode >> $2 + cat $1/*.swift >> $2 -cp ./Behavioral.swift ./Design-Patterns.playground/Pages/Behavioral.xcplaygroundpage/Contents.swift -cp ./Creational.swift ./Design-Patterns.playground/Pages/Creational.xcplaygroundpage/Contents.swift -cp ./Structural.swift ./Design-Patterns.playground/Pages/Structural.xcplaygroundpage/Contents.swift + { rm $2 && awk '{gsub("\\*//\\*:", "", $0); print}' > $2; } < $2 + { rm $2 && awk '{gsub("\\*/", "\n```swift", $0); print}' > $2; } < $2 + { rm $2 && awk '{gsub("/\\*:", "```\n", $0); print}' > $2; } < $2 + + cat source/endSwiftCode >> $2 -cat source/header.swift source/*/* source/footer.swift > ./contents.swift + { rm $2 && awk '{gsub("```swift```", "", $0); print}' > $2; } < $2 -cleanThisMessForReadme ./contents.swift + cat $2 >> README.md + rm $2 +} + +readme() { + combineMarkdown source/$1 $1.md +} -cp ./contents.swift ./README.md +playground Index +playground Behavioral +playground Creational +playground Structural zip -r -X Design-Patterns.playground.zip ./Design-Patterns.playground -rm ./Behavioral.swift -rm ./Creational.swift -rm ./Structural.swift -rm ./contents.swift \ No newline at end of file +echo "" > README.md + +readme Index +cat source/contentsReadme.md >> README.md +readme Behavioral +readme Creational +readme Structural +cat source/footer.md >> README.md \ No newline at end of file diff --git a/source-cn/Index/header.md b/source-cn/Index/header.md new file mode 100644 index 0000000..172eeb0 --- /dev/null +++ b/source-cn/Index/header.md @@ -0,0 +1,14 @@ + +设计模式(Swift 5.0 实现) +====================== + +([Design-Patterns-CN.playground.zip](https://raw.githubusercontent.com/ochococo/Design-Patterns-In-Swift/master/Design-Patterns-CN.playground.zip)). + +👷 源项目由 [@nsmeme](http://twitter.com/nsmeme) (Oktawian Chojnacki) 维护。 + +🇨🇳 中文版由 [@binglogo](https://twitter.com/binglogo) 整理翻译。 + +🚀 如何由源代码,并生成 README 与 Playground 产物,请查看: +- [CONTRIBUTING.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/CONTRIBUTING.md) +- [CONTRIBUTING-CN.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/CONTRIBUTING-CN.md) + diff --git a/source-cn/Index/welcome.swift b/source-cn/Index/welcome.swift new file mode 100644 index 0000000..8f396fe --- /dev/null +++ b/source-cn/Index/welcome.swift @@ -0,0 +1,2 @@ + +print("您好!") diff --git a/source-cn/behavioral/chain_of_responsibility.swift b/source-cn/behavioral/chain_of_responsibility.swift new file mode 100644 index 0000000..2ab3332 --- /dev/null +++ b/source-cn/behavioral/chain_of_responsibility.swift @@ -0,0 +1,96 @@ +/*: +🐝 责任链(Chain Of Responsibility) +------------------------------ + +责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。 + +### 示例: +*/ + +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 { + 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) + } +} +/*: + ### 用法 + */ +// 创建一系列的钱堆,并将其链接起来:10<20<50<100 +let ten = MoneyPile(value: 10, quantity: 6, next: nil) +let twenty = MoneyPile(value: 20, quantity: 2, next: ten) +let fifty = MoneyPile(value: 50, quantity: 2, next: twenty) +let hundred = MoneyPile(value: 100, quantity: 1, next: fifty) + +// 创建 ATM 实例 +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 - 1x100 diff --git a/source-cn/behavioral/command.swift b/source-cn/behavioral/command.swift new file mode 100644 index 0000000..a04e7ad --- /dev/null +++ b/source-cn/behavioral/command.swift @@ -0,0 +1,62 @@ +/*: +👫 命令(Command) + ------------ + 命令模式是一种设计模式,它尝试以对象来代表实际行动。命令对象可以把行动(action) 及其参数封装起来,于是这些行动可以被: + * 重复多次 + * 取消(如果该对象有实现的话) + * 取消后又再重做 + ### 示例: +*/ +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() + } +} +/*: +### 用法 +*/ +let podBayDoors = "Pod Bay Doors" +let doorModule = HAL9000DoorsOperations(doors:podBayDoors) + +doorModule.open() +doorModule.close() diff --git a/source-cn/behavioral/header.md b/source-cn/behavioral/header.md new file mode 100644 index 0000000..376496b --- /dev/null +++ b/source-cn/behavioral/header.md @@ -0,0 +1,7 @@ + + 行为型模式 + ======== + + >在软件工程中, 行为型模式为设计模式的一种类型,用来识别对象之间的常用交流模式并加以实现。如此,可在进行这些交流活动时增强弹性。 + > + >**来源:** [维基百科](https://zh.wikipedia.org/wiki/%E8%A1%8C%E7%82%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F) diff --git a/source-cn/behavioral/interpreter.swift b/source-cn/behavioral/interpreter.swift new file mode 100644 index 0000000..4cc8013 --- /dev/null +++ b/source-cn/behavioral/interpreter.swift @@ -0,0 +1,89 @@ +/*: +🎶 解释器(Interpreter) + ------------------ + + 给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。 + + ### 示例: +*/ + +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) diff --git a/source-cn/behavioral/iterator.swift b/source-cn/behavioral/iterator.swift new file mode 100644 index 0000000..ccc0381 --- /dev/null +++ b/source-cn/behavioral/iterator.swift @@ -0,0 +1,44 @@ +/*: +🍫 迭代器(Iterator) + --------------- + + 迭代器模式可以让用户通过特定的接口巡访容器中的每一个元素而不用了解底层的实现。 + + ### 示例: +*/ +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)") +} diff --git a/source-cn/behavioral/mediator.swift b/source-cn/behavioral/mediator.swift new file mode 100644 index 0000000..2ae0e6b --- /dev/null +++ b/source-cn/behavioral/mediator.swift @@ -0,0 +1,64 @@ +/*: +💐 中介者(Mediator) + --------------- + + 用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互。 + + ### 示例: +*/ +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) + diff --git a/source-cn/behavioral/memento.swift b/source-cn/behavioral/memento.swift new file mode 100644 index 0000000..6bbe9c6 --- /dev/null +++ b/source-cn/behavioral/memento.swift @@ -0,0 +1,83 @@ +/*: +💾 备忘录(Memento) +-------------- + +在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态 + +### 示例: +*/ +typealias Memento = [String: String] +/*: +发起人(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 ] + } +} +/*: +管理者(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) + } +} +/*: +### 用法 +*/ +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) +} diff --git a/source-cn/behavioral/observer.swift b/source-cn/behavioral/observer.swift new file mode 100644 index 0000000..b9e21ec --- /dev/null +++ b/source-cn/behavioral/observer.swift @@ -0,0 +1,49 @@ +/*: +👓 观察者(Observer) +--------------- + +一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知 + +### 示例: +*/ +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 diff --git a/source-cn/behavioral/state.swift b/source-cn/behavioral/state.swift new file mode 100644 index 0000000..37958f8 --- /dev/null +++ b/source-cn/behavioral/state.swift @@ -0,0 +1,58 @@ +/*: +🐉 状态(State) +--------- + +在状态模式中,对象的行为是基于它的内部状态而改变的。 +这个模式允许某个类对象在运行时发生改变。 + +### 示例: +*/ +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) diff --git a/source-cn/behavioral/strategy.swift b/source-cn/behavioral/strategy.swift new file mode 100644 index 0000000..bde6efa --- /dev/null +++ b/source-cn/behavioral/strategy.swift @@ -0,0 +1,61 @@ +/*: +💡 策略(Strategy) +-------------- + +对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。策略模式: +* 定义了一族算法(业务规则); +* 封装了每个算法; +* 这族的算法可互换代替(interchangeable)。 + +### 示例: +*/ + +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) + } +} + +/*: + ### 用法 + */ + +let rachel = TestSubject(pupilDiameter: 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) diff --git a/source-cn/behavioral/template_method.swift b/source-cn/behavioral/template_method.swift new file mode 100644 index 0000000..13c34eb --- /dev/null +++ b/source-cn/behavioral/template_method.swift @@ -0,0 +1,49 @@ +/*: +📝 模板方法模式 +----------- + + 模板方法模式是一种行为设计模式, 它通过父类/协议中定义了一个算法的框架, 允许子类/具体实现对象在不修改结构的情况下重写算法的特定步骤。 + +### 示例: +*/ +protocol Garden { + func prepareSoil() + func plantSeeds() + func waterPlants() + func prepareGarden() +} + +extension Garden { + + func prepareGarden() { + prepareSoil() + plantSeeds() + waterPlants() + } +} + +final class RoseGarden: Garden { + + func prepare() { + prepareGarden() + } + + func prepareSoil() { + print ("prepare soil for rose garden") + } + + func plantSeeds() { + print ("plant seeds for rose garden") + } + + func waterPlants() { + print ("water the rose garden") + } +} + +/*: +### 用法 +*/ + +let roseGarden = RoseGarden() +roseGarden.prepare() diff --git a/source-cn/behavioral/visitor.swift b/source-cn/behavioral/visitor.swift new file mode 100644 index 0000000..14513ba --- /dev/null +++ b/source-cn/behavioral/visitor.swift @@ -0,0 +1,57 @@ +/*: +🏃 访问者(Visitor) +-------------- + +封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 + +### 示例: +*/ +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" } +} + +/*: +### 用法 +*/ +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 +} + +names diff --git a/source-cn/contents.md b/source-cn/contents.md new file mode 100644 index 0000000..8b8750e --- /dev/null +++ b/source-cn/contents.md @@ -0,0 +1,6 @@ + +## 目录 + +* [行为型模式](Behavioral) +* [创建型模式](Creational) +* [结构型模式](Structural) \ No newline at end of file diff --git a/source-cn/contentsReadme.md b/source-cn/contentsReadme.md new file mode 100644 index 0000000..489f592 --- /dev/null +++ b/source-cn/contentsReadme.md @@ -0,0 +1,16 @@ + +## 目录 + +| [行为型模式](#行为型模式) | [创建型模式](#创建型模式) | [结构型模式](#结构型模式structural) | +| ------------------------------------------------------------ | --------------------------------------------------------- | ------------------------------------------------------------ | +| [🐝 责任链 Chain Of Responsibility](#-责任链chain-of-responsibility) | [🌰 抽象工厂 Abstract Factory](#-抽象工厂abstract-factory) | [🔌 适配器 Adapter](#-适配器adapter) | +| [👫 命令 Command](#-命令command) | [👷 生成器 Builder](#-生成器builder) | [🌉 桥接 Bridge](#-桥接bridge) | +| [🎶 解释器 Interpreter](#-解释器interpreter) | [🏭 工厂方法 Factory Method](#-工厂方法factory-method) | [🌿 组合 Composite](#-组合composite) | +| [🍫 迭代器 Iterator](#-迭代器iterator) | [🔂 单态 Monostate](#-单态monostate) | [🍧 修饰 Decorator](#-修饰decorator) | +| [💐 中介者 Mediator](#-中介者mediator) | [🃏 原型 Prototype](#-原型prototype) | [🎁 外观 Façade](#-外观facade) | +| [💾 备忘录 Memento](#-备忘录memento) | [💍 单例 Singleton](#-单例singleton) | [🍃 享元 Flyweight](#-享元flyweight) | +| [👓 观察者 Observer](#-观察者observer) | | [☔ 保护代理 Protection Proxy](#-保护代理模式protection-proxy) | +| [🐉 状态 State](#-状态state) | | [🍬 虚拟代理 Virtual Proxy](#-虚拟代理virtual-proxy) | +| [💡 策略 Strategy](#-策略strategy) | | | +| [📝 模板方法 Templdate Method](#-template-method) | | | +| [🏃 访问者 Visitor](#-访问者visitor) | | | diff --git a/source-cn/creational/abstract_factory.swift b/source-cn/creational/abstract_factory.swift new file mode 100644 index 0000000..4586869 --- /dev/null +++ b/source-cn/creational/abstract_factory.swift @@ -0,0 +1,60 @@ +/*: +🌰 抽象工厂(Abstract Factory) +------------- + +抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。在正常使用中,客户端程序需要创建抽象工厂的具体实现,然后使用抽象工厂作为接口来创建这一主题的具体对象。 + +### 示例: + +协议 +*/ + +protocol BurgerDescribing { + var ingredients: [String] { get } +} + +struct CheeseBurger: BurgerDescribing { + let ingredients: [String] +} + +protocol BurgerMaking { + func make() -> BurgerDescribing +} + +// 工厂方法实现 + +final class BigKahunaBurger: 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"]) + } +} + +/*: +抽象工厂 +*/ + +enum BurgerFactoryType: BurgerMaking { + + case bigKahuna + case jackInTheBox + + func make() -> BurgerDescribing { + switch self { + case .bigKahuna: + return BigKahunaBurger().make() + case .jackInTheBox: + return JackInTheBox().make() + } + } +} +/*: +### 用法 +*/ +let bigKahuna = BurgerFactoryType.bigKahuna.make() +let jackInTheBox = BurgerFactoryType.jackInTheBox.make() diff --git a/source-cn/creational/builder.swift b/source-cn/creational/builder.swift new file mode 100644 index 0000000..4de8bee --- /dev/null +++ b/source-cn/creational/builder.swift @@ -0,0 +1,52 @@ +/*: +👷 生成器(Builder) +-------------- + +一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。 + +### 示例: +*/ +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))" + } +} +/*: +### 用法 +*/ +let empire = DeathStarBuilder { builder in + builder.x = 0.1 + builder.y = 0.2 + builder.z = 0.3 +} + +let deathStar = DeathStar(builder:empire) diff --git a/source-cn/creational/factory.swift b/source-cn/creational/factory.swift new file mode 100644 index 0000000..916b40c --- /dev/null +++ b/source-cn/creational/factory.swift @@ -0,0 +1,63 @@ +/*: +🏭 工厂方法(Factory Method) +----------------------- + +定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。 + +### 示例: +*/ +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 + } + + } +} +/*: +### 用法 +*/ +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 diff --git a/source-cn/creational/header.md b/source-cn/creational/header.md new file mode 100644 index 0000000..0a553b7 --- /dev/null +++ b/source-cn/creational/header.md @@ -0,0 +1,8 @@ + + 创建型模式 + ======== + + > 创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。 + > + >**来源:** [维基百科](https://zh.wikipedia.org/wiki/%E5%89%B5%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F) + \ No newline at end of file diff --git a/source-cn/creational/monostate.swift b/source-cn/creational/monostate.swift new file mode 100644 index 0000000..74898ef --- /dev/null +++ b/source-cn/creational/monostate.swift @@ -0,0 +1,39 @@ +/*: + 🔂 单态(Monostate) + ------------ + + 单态模式是实现单一共享的另一种方法。不同于单例模式,它通过完全不同的机制,在不限制构造方法的情况下实现单一共享特性。 + 因此,在这种情况下,单态会将状态保存为静态,而不是将整个实例保存为单例。 + [单例和单态 - Robert C. Martin](http://staff.cs.utu.fi/~jounsmed/doos_06/material/SingletonAndMonostate.pdf) + +### 示例: +*/ +class Settings { + + enum Theme { + case `default` + case old + case new + } + + private static var theme: Theme? + + var currentTheme: Theme { + get { Settings.theme ?? .default } + set(newTheme) { Settings.theme = newTheme } + } +} +/*: +### 用法: +*/ +import SwiftUI + +// 改变主题 +let settings = Settings() // 开始使用主题 .old +settings.currentTheme = .new // 改变主题为 .new + +// 界面一 +let screenColor: Color = Settings().currentTheme == .old ? .gray : .white + +// 界面二 +let screenTitle: String = Settings().currentTheme == .old ? "Itunes Connect" : "App Store Connect" diff --git a/source-cn/creational/prototype.swift b/source-cn/creational/prototype.swift new file mode 100644 index 0000000..c9edbaa --- /dev/null +++ b/source-cn/creational/prototype.swift @@ -0,0 +1,34 @@ +/*: +🃏 原型(Prototype) +-------------- + +通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。 + +### 示例: +*/ +class MoonWorker { + + let name: String + var health: Int = 100 + + init(name: String) { + self.name = name + } + + func clone() -> MoonWorker { + return MoonWorker(name: name) + } +} +/*: +### 用法 +*/ +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 = 0 diff --git a/source-cn/creational/singleton.swift b/source-cn/creational/singleton.swift new file mode 100644 index 0000000..719360d --- /dev/null +++ b/source-cn/creational/singleton.swift @@ -0,0 +1,20 @@ +/*: +💍 单例(Singleton) +-------------- + +单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为 + +### 示例: +*/ +final class ElonMusk { + + static let shared = ElonMusk() + + private init() { + // Private initialization to ensure just one instance is created. + } +} +/*: +### 用法 +*/ +let elon = ElonMusk.shared // There is only one Elon Musk folks. diff --git a/source-cn/endComment b/source-cn/endComment new file mode 100644 index 0000000..9f82b4e --- /dev/null +++ b/source-cn/endComment @@ -0,0 +1,2 @@ + +*/ \ No newline at end of file diff --git a/source-cn/endSwiftCode b/source-cn/endSwiftCode new file mode 100644 index 0000000..7dc72a8 --- /dev/null +++ b/source-cn/endSwiftCode @@ -0,0 +1,2 @@ +``` + diff --git a/source-cn/footer.md b/source-cn/footer.md new file mode 100644 index 0000000..21002da --- /dev/null +++ b/source-cn/footer.md @@ -0,0 +1,5 @@ + +Info +==== + +📖 Descriptions from: [Gang of Four Design Patterns Reference Sheet](http://www.blackwasp.co.uk/GangOfFour.aspx) diff --git a/source-cn/imports.swift b/source-cn/imports.swift new file mode 100644 index 0000000..350c82f --- /dev/null +++ b/source-cn/imports.swift @@ -0,0 +1,2 @@ + +import Foundation diff --git a/source-cn/startComment b/source-cn/startComment new file mode 100644 index 0000000..a23f02a --- /dev/null +++ b/source-cn/startComment @@ -0,0 +1 @@ +/*: diff --git a/source-cn/startSwiftCode b/source-cn/startSwiftCode new file mode 100644 index 0000000..591b2be --- /dev/null +++ b/source-cn/startSwiftCode @@ -0,0 +1,3 @@ + + +```swift \ No newline at end of file diff --git a/source-cn/structural/adapter.swift b/source-cn/structural/adapter.swift new file mode 100644 index 0000000..c654c03 --- /dev/null +++ b/source-cn/structural/adapter.swift @@ -0,0 +1,51 @@ +/*: +🔌 适配器(Adapter) +-------------- + +适配器模式有时候也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。 + +### 示例: +*/ +protocol NewDeathStarSuperLaserAiming { + var angleV: Double { get } + var angleH: Double { get } +} +/*: +**被适配者** +*/ +struct OldDeathStarSuperlaserTarget { + let angleHorizontal: Float + let angleVertical: Float + + init(angleHorizontal: Float, angleVertical: Float) { + self.angleHorizontal = angleHorizontal + self.angleVertical = angleVertical + } +} +/*: +**适配器** +*/ +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 + } +} +/*: +### 用法 +*/ +let target = OldDeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) +let newFormat = NewDeathStarSuperlaserTarget(target) + +newFormat.angleH +newFormat.angleV diff --git a/source-cn/structural/bridge.swift b/source-cn/structural/bridge.swift new file mode 100644 index 0000000..e9f8979 --- /dev/null +++ b/source-cn/structural/bridge.swift @@ -0,0 +1,48 @@ +/*: +🌉 桥接(Bridge) +----------- + +桥接模式将抽象部分与实现部分分离,使它们都可以独立的变化。 + +### 示例: +*/ +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") + } +} +/*: +### 用法 +*/ +let tvRemoteControl = RemoteControl(appliance: TV()) +tvRemoteControl.turnOn() + +let fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner()) +fancyVacuumCleanerRemoteControl.turnOn() diff --git a/source-cn/structural/composite.swift b/source-cn/structural/composite.swift new file mode 100644 index 0000000..8d771e2 --- /dev/null +++ b/source-cn/structural/composite.swift @@ -0,0 +1,50 @@ +/*: +🌿 组合(Composite) +-------------- + +将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 + +### 示例: + +组件(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)") + } +} + +/*: +组合 +*/ +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) + } + } +} +/*: +### 用法 +*/ +var whiteboard = Whiteboard(Circle(), Square()) +whiteboard.draw(fillColor: "Red") diff --git a/source-cn/structural/decorator.swift b/source-cn/structural/decorator.swift new file mode 100644 index 0000000..7b47e39 --- /dev/null +++ b/source-cn/structural/decorator.swift @@ -0,0 +1,62 @@ +/*: +🍧 修饰(Decorator) +-------------- + +修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。 +就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。 + +### 示例: +*/ +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 ingredients: [String] { + return beverage.ingredients + ["Milk"] + } +} + +struct WhipCoffee: BeverageHaving { + + let beverage: BeverageDataHaving + + var cost: Double { + return beverage.cost + 0.5 + } + + var ingredients: [String] { + return beverage.ingredients + ["Whip"] + } +} +/*: +### 用法 +*/ +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)") diff --git a/source-cn/structural/facade.swift b/source-cn/structural/facade.swift new file mode 100644 index 0000000..f9a3b42 --- /dev/null +++ b/source-cn/structural/facade.swift @@ -0,0 +1,36 @@ +/*: +🎁 外观(Facade) +----------- + +外观模式为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。 + +### 示例: +*/ +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) + } + } +} +/*: +### 用法 +*/ +let storage = Defaults() + +// Store +storage["Bishop"] = "Disconnect me. I’d rather be nothing" + +// Read +storage["Bishop"] diff --git a/source-cn/structural/flyweight.swift b/source-cn/structural/flyweight.swift new file mode 100644 index 0000000..97ac692 --- /dev/null +++ b/source-cn/structural/flyweight.swift @@ -0,0 +1,58 @@ +/*: +🍃 享元(Flyweight) +-------------- + +使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。 + +### 示例: +*/ +// 特指咖啡生成的对象会是享元 +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)") + } + } +} +/*: +### 用法 +*/ +let coffeeShop = CoffeeShop(menu: Menu()) + +coffeeShop.takeOrder(origin: "Yirgacheffe, Ethiopia", table: 1) +coffeeShop.takeOrder(origin: "Buziraguhindwa, Burundi", table: 3) + +coffeeShop.serve() diff --git a/source-cn/structural/header.md b/source-cn/structural/header.md new file mode 100644 index 0000000..7b27407 --- /dev/null +++ b/source-cn/structural/header.md @@ -0,0 +1,7 @@ + +结构型模式(Structural) +==================== + +> 在软件工程中结构型模式是设计模式,借由一以贯之的方式来了解元件间的关系,以简化设计。 +> +>**来源:** [维基百科](https://zh.wikipedia.org/wiki/%E7%B5%90%E6%A7%8B%E5%9E%8B%E6%A8%A1%E5%BC%8F) diff --git a/source-cn/structural/protection_proxy.swift b/source-cn/structural/protection_proxy.swift new file mode 100644 index 0000000..dcb519e --- /dev/null +++ b/source-cn/structural/protection_proxy.swift @@ -0,0 +1,52 @@ +/*: +☔ 保护代理模式(Protection Proxy) +------------------ + +在代理模式中,创建一个类代表另一个底层类的功能。 +保护代理用于限制访问。 + +### 示例: +*/ +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) + } +} +/*: +### 用法 +*/ +let computer = CurrentComputer() +let podBay = "Pod Bay Doors" + +computer.open(doors: podBay) + +computer.authenticate(password: "pass") +computer.open(doors: podBay) diff --git a/source-cn/structural/virtual_proxy.swift b/source-cn/structural/virtual_proxy.swift new file mode 100644 index 0000000..c17233a --- /dev/null +++ b/source-cn/structural/virtual_proxy.swift @@ -0,0 +1,32 @@ +/*: +🍬 虚拟代理(Virtual Proxy) +---------------- + +在代理模式中,创建一个类代表另一个底层类的功能。 +虚拟代理用于对象的需时加载。 + +### 示例: +*/ +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() + } +} +/*: +### 用法 +*/ +let humanInterface = HEVSuitHumanInterface() +humanInterface.administerMorphine() diff --git a/source/Index/header.md b/source/Index/header.md new file mode 100644 index 0000000..2c48e66 --- /dev/null +++ b/source/Index/header.md @@ -0,0 +1,13 @@ + +Design Patterns implemented in Swift 5.0 +======================================== + +A short cheat-sheet with Xcode 10.2 Playground ([Design-Patterns.playground.zip](https://raw.githubusercontent.com/ochococo/Design-Patterns-In-Swift/master/Design-Patterns.playground.zip)). + +### [🇨🇳中文版](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/README-CN.md) + +👷 Project started by: [@nsmeme](http://twitter.com/nsmeme) (Oktawian Chojnacki) + +👷 中文版由 [@binglogo](https://twitter.com/binglogo) (棒棒彬) 整理翻译。 + +🚀 How to generate README, Playground and zip from source: [CONTRIBUTING.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/CONTRIBUTING.md) diff --git a/source/Index/welcome.swift b/source/Index/welcome.swift new file mode 100644 index 0000000..a707971 --- /dev/null +++ b/source/Index/welcome.swift @@ -0,0 +1,2 @@ + +print("Welcome!") diff --git a/source/behavioral/chain_of_responsibility.swift b/source/behavioral/chain_of_responsibility.swift index b2909b1..e2dd24d 100644 --- a/source/behavioral/chain_of_responsibility.swift +++ b/source/behavioral/chain_of_responsibility.swift @@ -6,87 +6,91 @@ The chain of responsibility pattern is used to process varied requests, each of ### Example: */ -class MoneyPile { + +protocol Withdrawing { + func withdraw(amount: Int) -> Bool +} + +final class MoneyPile: Withdrawing { + let value: Int var quantity: Int - var nextPile: MoneyPile? - - init(value: Int, quantity: Int, nextPile: MoneyPile?) { + var next: Withdrawing? + + init(value: Int, quantity: Int, next: Withdrawing?) { self.value = value self.quantity = quantity - self.nextPile = nextPile + self.next = next } - - func canWithdraw(v: Int) -> Bool { - var v = v + func withdraw(amount: Int) -> Bool { + + var amount = amount func canTakeSomeBill(want: Int) -> Bool { return (want / self.value) > 0 } - - var q = self.quantity - while canTakeSomeBill(v) { + var quantity = self.quantity - if q == 0 { + while canTakeSomeBill(want: amount) { + + if quantity == 0 { break } - v -= self.value - q -= 1 + amount -= self.value + quantity -= 1 } - if v == 0 { + guard amount > 0 else { return true - } else if let next = self.nextPile { - return next.canWithdraw(v) + } + + if let next { + return next.withdraw(amount: amount) } return false } } -class ATM { - private var hundred: MoneyPile - private var fifty: MoneyPile - private var twenty: MoneyPile - private var ten: MoneyPile - - private var startPile: MoneyPile { +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: MoneyPile, - fifty: MoneyPile, - twenty: MoneyPile, - ten: MoneyPile) { + + init(hundred: Withdrawing, + fifty: Withdrawing, + twenty: Withdrawing, + ten: Withdrawing) { self.hundred = hundred self.fifty = fifty self.twenty = twenty self.ten = ten } - - func canWithdraw(value: Int) -> String { - return "Can withdraw: \(self.startPile.canWithdraw(value))" + + func withdraw(amount: Int) -> Bool { + return startPile.withdraw(amount: amount) } } /*: ### Usage */ // 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) +let ten = MoneyPile(value: 10, quantity: 6, next: nil) +let twenty = MoneyPile(value: 20, quantity: 2, next: ten) +let fifty = MoneyPile(value: 50, quantity: 2, next: twenty) +let hundred = MoneyPile(value: 100, quantity: 1, next: fifty) // Build ATM. var atm = ATM(hundred: hundred, fifty: fifty, twenty: twenty, ten: ten) -atm.canWithdraw(310) // Cannot because ATM has only 300 -atm.canWithdraw(100) // Can withdraw - 1x100 -atm.canWithdraw(165) // Cannot withdraw because ATM doesn't has bill with value of 5 -atm.canWithdraw(30) // Can withdraw - 1x20, 2x10 -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Chain-Of-Responsibility) -*/ +atm.withdraw(amount: 310) // Cannot because ATM has only 300 +atm.withdraw(amount: 100) // Can withdraw - 1x100 diff --git a/source/behavioral/command.swift b/source/behavioral/command.swift index 0b1b122..c092a3e 100644 --- a/source/behavioral/command.swift +++ b/source/behavioral/command.swift @@ -10,7 +10,7 @@ protocol DoorCommand { func execute() -> String } -class OpenCommand : DoorCommand { +final class OpenCommand: DoorCommand { let doors:String required init(doors: String) { @@ -22,7 +22,7 @@ class OpenCommand : DoorCommand { } } -class CloseCommand : DoorCommand { +final class CloseCommand: DoorCommand { let doors:String required init(doors: String) { @@ -34,7 +34,7 @@ class CloseCommand : DoorCommand { } } -class HAL9000DoorsOperations { +final class HAL9000DoorsOperations { let openCommand: DoorCommand let closeCommand: DoorCommand diff --git a/source/behavioral/_title.swift b/source/behavioral/header.md similarity index 75% rename from source/behavioral/_title.swift rename to source/behavioral/header.md index 8b29bd2..61e1045 100644 --- a/source/behavioral/_title.swift +++ b/source/behavioral/header.md @@ -1,13 +1,7 @@ -//: Behavioral | -//: [Creational](Creational) | -//: [Structural](Structural) -/*: + Behavioral ========== >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](http://en.wikipedia.org/wiki/Behavioral_pattern) -*/ -import Swift -import Foundation diff --git a/source/behavioral/interpreter.swift b/source/behavioral/interpreter.swift index 20408e1..09427b3 100644 --- a/source/behavioral/interpreter.swift +++ b/source/behavioral/interpreter.swift @@ -7,87 +7,83 @@ The interpreter pattern is used to evaluate sentences in a language. ### Example */ -protocol IntegerExp { - func evaluate(context: IntegerContext) -> Int - func replace(character: Character, integerExp: IntegerExp) -> IntegerExp - func copy() -> IntegerExp +protocol IntegerExpression { + func evaluate(_ context: IntegerContext) -> Int + func replace(character: Character, integerExpression: IntegerExpression) -> IntegerExpression + func copied() -> IntegerExpression } -class IntegerContext { +final class IntegerContext { private var data: [Character:Int] = [:] - + func lookup(name: Character) -> Int { return self.data[name]! } - - func assign(integerVarExp: IntegerVarExp, value: Int) { - self.data[integerVarExp.name] = value + + func assign(expression: IntegerVariableExpression, value: Int) { + self.data[expression.name] = value } } -class IntegerVarExp: IntegerExp { +final class IntegerVariableExpression: IntegerExpression { let name: Character - + init(name: Character) { self.name = name } - - func evaluate(context: IntegerContext) -> Int { - return context.lookup(self.name) + + func evaluate(_ context: IntegerContext) -> Int { + return context.lookup(name: self.name) } - - func replace(name: Character, integerExp: IntegerExp) -> IntegerExp { + + func replace(character name: Character, integerExpression: IntegerExpression) -> IntegerExpression { if name == self.name { - return integerExp.copy() + return integerExpression.copied() } else { - return IntegerVarExp(name: self.name) + return IntegerVariableExpression(name: self.name) } } - - func copy() -> IntegerExp { - return IntegerVarExp(name: self.name) + + func copied() -> IntegerExpression { + return IntegerVariableExpression(name: self.name) } } -class AddExp: IntegerExp { - private var operand1: IntegerExp - private var operand2: IntegerExp - - init(op1: IntegerExp, op2: IntegerExp) { +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 { + + func evaluate(_ context: IntegerContext) -> Int { return self.operand1.evaluate(context) + self.operand2.evaluate(context) } - - func replace(character: Character, integerExp: IntegerExp) -> IntegerExp { - return AddExp(op1: operand1.replace(character, integerExp: integerExp), - op2: operand2.replace(character, integerExp: integerExp)) + + func replace(character: Character, integerExpression: IntegerExpression) -> IntegerExpression { + return AddExpression(op1: operand1.replace(character: character, integerExpression: integerExpression), + op2: operand2.replace(character: character, integerExpression: integerExpression)) } - - func copy() -> IntegerExp { - return AddExp(op1: self.operand1, op2: self.operand2) + + func copied() -> IntegerExpression { + return AddExpression(op1: self.operand1, op2: self.operand2) } } /*: ### Usage */ -var expression: IntegerExp? -var intContext = IntegerContext() +var context = IntegerContext() -var a = IntegerVarExp(name: "A") -var b = IntegerVarExp(name: "B") -var c = IntegerVarExp(name: "C") +var a = IntegerVariableExpression(name: "A") +var b = IntegerVariableExpression(name: "B") +var c = IntegerVariableExpression(name: "C") -expression = AddExp(op1: a, op2: AddExp(op1: b, op2: c)) // a + (b + c) +var expression = AddExpression(op1: a, op2: AddExpression(op1: b, op2: c)) // a + (b + c) -intContext.assign(a, value: 2) -intContext.assign(b, value: 1) -intContext.assign(c, value: 3) +context.assign(expression: a, value: 2) +context.assign(expression: b, value: 1) +context.assign(expression: c, value: 3) -var result = expression?.evaluate(intContext) -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Interpreter) -*/ +var result = expression.evaluate(context) diff --git a/source/behavioral/iterator.swift b/source/behavioral/iterator.swift index ca19f9c..d351958 100644 --- a/source/behavioral/iterator.swift +++ b/source/behavioral/iterator.swift @@ -6,22 +6,38 @@ The iterator pattern is used to provide a standard interface for traversing a co ### Example: */ -struct NovellasCollection { - let novellas: [T] +struct Novella { + let name: String } -extension NovellasCollection: SequenceType { - typealias Generator = AnyGenerator - - func generate() -> AnyGenerator { - var i = 0 - return AnyGenerator { i += 1; return i >= self.novellas.count ? nil : self.novellas[i] } +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) } } /*: ### Usage */ -let greatNovellas = NovellasCollection(novellas:["Mist"]) +let greatNovellas = Novellas(novellas: [Novella(name: "The Mist")] ) for novella in greatNovellas { print("I've read: \(novella)") diff --git a/source/behavioral/mediator.swift b/source/behavioral/mediator.swift index b24b844..07648f5 100644 --- a/source/behavioral/mediator.swift +++ b/source/behavioral/mediator.swift @@ -6,62 +6,59 @@ The mediator pattern is used to reduce coupling between classes that communicate ### Example */ +protocol Receiver { + associatedtype MessageType + func receive(message: MessageType) +} + +protocol Sender { + associatedtype MessageType + associatedtype ReceiverType: Receiver + + var recipients: [ReceiverType] { get } + + func send(message: MessageType) +} -class Colleague { +struct Programmer: Receiver { let name: String - let mediator: Mediator - init(name: String, mediator: Mediator) { + init(name: String) { self.name = name - self.mediator = mediator - } - - func send(message: String) { - mediator.send(message, colleague: self) } func receive(message: String) { - assert(false, "Method should be overriden") + print("\(name) received: \(message)") } } -protocol Mediator { - func send(message: String, colleague: Colleague) -} - -class MessageMediator: Mediator { - private var colleagues: [Colleague] = [] +final class MessageMediator: Sender { + internal var recipients: [Programmer] = [] - func addColleague(colleague: Colleague) { - colleagues.append(colleague) + func add(recipient: Programmer) { + recipients.append(recipient) } - func send(message: String, colleague: Colleague) { - for c in colleagues { - if c !== colleague { //for simplicity we compare object references - c.receive(message) - } + func send(message: String) { + for recipient in recipients { + recipient.receive(message: message) } } } -class ConcreteColleague: Colleague { - override func receive(message: String) { - print("Colleague \(name) received: \(message)") - } -} - /*: ### Usage */ +func spamMonster(message: String, worker: MessageMediator) { + worker.send(message: message) +} let messagesMediator = MessageMediator() -let user0 = ConcreteColleague(name: "0", mediator: messagesMediator) -let user1 = ConcreteColleague(name: "1", mediator: messagesMediator) -messagesMediator.addColleague(user0) -messagesMediator.addColleague(user1) -user0.send("Hello") // user1 receives message -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Mediator) -*/ +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) + diff --git a/source/behavioral/memento.swift b/source/behavioral/memento.swift index 6baba83..13b0474 100644 --- a/source/behavioral/memento.swift +++ b/source/behavioral/memento.swift @@ -6,64 +6,78 @@ The memento pattern is used to capture the current state of an object and store ### Example */ -typealias Memento = Dictionary - -let DPMementoKeyChapter = "com.valve.halflife.chapter" -let DPMementoKeyWeapon = "com.valve.halflife.weapon" -let DPMementoGameState = "com.valve.halflife.state" +typealias Memento = [String: String] /*: Originator */ -class GameState { - var chapter: String = "" - var weapon: String = "" +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 - func toMemento() -> Memento { - return [ DPMementoKeyChapter:chapter, DPMementoKeyWeapon:weapon ] + init(chapter: String, weapon: String) { + self.chapter = chapter + self.weapon = weapon } - func restoreFromMemento(memento: Memento) { - chapter = memento[DPMementoKeyChapter] as? String ?? "n/a" - weapon = memento[DPMementoKeyWeapon] as? String ?? "n/a" + 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 ] } } /*: Caretaker */ enum CheckPoint { - static func saveState(memento: Memento, keyName: String = DPMementoGameState) { - let defaults = NSUserDefaults.standardUserDefaults() - defaults.setObject(memento, forKey: keyName) + + private static let defaults = UserDefaults.standard + + static func save(_ state: MementoConvertible, saveName: String) { + defaults.set(state.memento, forKey: saveName) defaults.synchronize() } - static func restorePreviousState(keyName keyName: String = DPMementoGameState) -> Memento { - let defaults = NSUserDefaults.standardUserDefaults() - - return defaults.objectForKey(keyName) as? Memento ?? Memento() + static func restore(saveName: String) -> Any? { + return defaults.object(forKey: saveName) } } /*: - ### Usage +### Usage */ -var gameState = GameState() -gameState.restoreFromMemento(CheckPoint.restorePreviousState()) - -gameState.chapter = "Black Mesa Inbound" -gameState.weapon = "Crowbar" -CheckPoint.saveState(gameState.toMemento()) +var gameState = GameState(chapter: "Black Mesa Inbound", weapon: "Crowbar") gameState.chapter = "Anomalous Materials" gameState.weapon = "Glock 17" -gameState.restoreFromMemento(CheckPoint.restorePreviousState()) +CheckPoint.save(gameState, saveName: "gameState1") gameState.chapter = "Unforeseen Consequences" gameState.weapon = "MP5" -CheckPoint.saveState(gameState.toMemento(), keyName: "gameState2") +CheckPoint.save(gameState, saveName: "gameState2") gameState.chapter = "Office Complex" gameState.weapon = "Crossbow" -CheckPoint.saveState(gameState.toMemento()) - -gameState.restoreFromMemento(CheckPoint.restorePreviousState(keyName: "gameState2")) +CheckPoint.save(gameState, saveName: "gameState3") +if let memento = CheckPoint.restore(saveName: "gameState1") as? Memento { + let finalState = GameState(memento: memento) + dump(finalState) +} diff --git a/source/behavioral/observer.swift b/source/behavioral/observer.swift index 2865485..aec0ac8 100644 --- a/source/behavioral/observer.swift +++ b/source/behavioral/observer.swift @@ -2,38 +2,40 @@ 👓 Observer ----------- -The observer pattern is used to allow an object to publish changes to its state. +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. ### Example */ protocol PropertyObserver : class { - func willChangePropertyName(propertyName:String, newPropertyValue:AnyObject?) - func didChangePropertyName(propertyName:String, oldPropertyValue:AnyObject?) + func willChange(propertyName: String, newPropertyValue: Any?) + func didChange(propertyName: String, oldPropertyValue: Any?) } -class TestChambers { +final class TestChambers { weak var observer:PropertyObserver? + private let testChamberNumberName = "testChamberNumber" + var testChamberNumber: Int = 0 { willSet(newValue) { - observer?.willChangePropertyName("testChamberNumber", newPropertyValue:newValue) + observer?.willChange(propertyName: testChamberNumberName, newPropertyValue: newValue) } didSet { - observer?.didChangePropertyName("testChamberNumber", oldPropertyValue:oldValue) + observer?.didChange(propertyName: testChamberNumberName, oldPropertyValue: oldValue) } } } -class Observer : PropertyObserver { - func willChangePropertyName(propertyName: String, newPropertyValue: AnyObject?) { +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 didChangePropertyName(propertyName: String, oldPropertyValue: AnyObject?) { + 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.") } @@ -45,7 +47,4 @@ class Observer : PropertyObserver { var observerInstance = Observer() var testChambers = TestChambers() testChambers.observer = observerInstance -testChambers.testChamberNumber++ -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Observer) -*/ +testChambers.testChamberNumber += 1 diff --git a/source/behavioral/state.swift b/source/behavioral/state.swift index c882258..14d84c1 100644 --- a/source/behavioral/state.swift +++ b/source/behavioral/state.swift @@ -2,30 +2,29 @@ 🐉 State --------- -The state pattern is used to alter the behaviour of an object as its internal state changes. +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. ### Example */ -class Context { +final class Context { private var state: State = UnauthorizedState() var isAuthorized: Bool { - get { return state.isAuthorized(self) } + get { return state.isAuthorized(context: self) } } var userId: String? { - get { return state.userId(self) } + get { return state.userId(context: self) } } - func changeStateToAuthorized(userId userId: String) { + func changeStateToAuthorized(userId: String) { state = AuthorizedState(userId: userId) } func changeStateToUnauthorized() { state = UnauthorizedState() } - } protocol State { @@ -51,12 +50,9 @@ class AuthorizedState: State { /*: ### Usage */ -let context = Context() -(context.isAuthorized, context.userId) -context.changeStateToAuthorized(userId: "admin") -(context.isAuthorized, context.userId) // now logged in as "admin" -context.changeStateToUnauthorized() -(context.isAuthorized, context.userId) -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-State) -*/ +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) diff --git a/source/behavioral/strategy.swift b/source/behavioral/strategy.swift index 5a395e8..f358961 100644 --- a/source/behavioral/strategy.swift +++ b/source/behavioral/strategy.swift @@ -6,42 +6,53 @@ The strategy pattern is used to create an interchangeable family of algorithms f ### Example */ -protocol PrintStrategy { - func printString(string: String) -> String + +struct TestSubject { + let pupilDiameter: Double + let blushResponse: Double + let isOrganic: Bool } -class Printer { +protocol RealnessTesting: AnyObject { + func testRealness(_ testSubject: TestSubject) -> Bool +} - let strategy: PrintStrategy - - func printString(string: String) -> String { - return self.strategy.printString(string) - } - - init(strategy: PrintStrategy) { - self.strategy = strategy +final class VoightKampffTest: RealnessTesting { + func testRealness(_ testSubject: TestSubject) -> Bool { + return testSubject.pupilDiameter < 30.0 || testSubject.blushResponse == 0.0 } } -class UpperCaseStrategy : PrintStrategy { - func printString(string:String) -> String { - return string.uppercaseString +final class GeneticTest: RealnessTesting { + func testRealness(_ testSubject: TestSubject) -> Bool { + return testSubject.isOrganic } } -class LowerCaseStrategy : PrintStrategy { - func printString(string:String) -> String { - return string.lowercaseString +final class BladeRunner { + private let strategy: RealnessTesting + + init(test: RealnessTesting) { + self.strategy = test + } + + func testIfAndroid(_ testSubject: TestSubject) -> Bool { + return !strategy.testRealness(testSubject) } } -/*: -### Usage -*/ -var lower = Printer(strategy:LowerCaseStrategy()) -lower.printString("O tempora, o mores!") -var upper = Printer(strategy:UpperCaseStrategy()) -upper.printString("O tempora, o mores!") /*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Strategy) -*/ + ### Usage + */ + +let rachel = TestSubject(pupilDiameter: 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) diff --git a/source/behavioral/template_method.swift b/source/behavioral/template_method.swift new file mode 100644 index 0000000..00eb243 --- /dev/null +++ b/source/behavioral/template_method.swift @@ -0,0 +1,49 @@ +/*: +📝 Template Method +----------- + + The template method pattern defines the steps of an algorithm and allows the redefinition of one or more of these steps. In this way, the template method protects the algorithm, the order of execution and provides abstract methods that can be implemented by concrete types. + +### Example +*/ +protocol Garden { + func prepareSoil() + func plantSeeds() + func waterPlants() + func prepareGarden() +} + +extension Garden { + + func prepareGarden() { + prepareSoil() + plantSeeds() + waterPlants() + } +} + +final class RoseGarden: Garden { + + func prepare() { + prepareGarden() + } + + func prepareSoil() { + print ("prepare soil for rose garden") + } + + func plantSeeds() { + print ("plant seeds for rose garden") + } + + func waterPlants() { + print ("water the rose garden") + } +} + +/*: +### Usage +*/ + +let roseGarden = RoseGarden() +roseGarden.prepare() diff --git a/source/behavioral/visitor.swift b/source/behavioral/visitor.swift index 694df96..d53e409 100644 --- a/source/behavioral/visitor.swift +++ b/source/behavioral/visitor.swift @@ -10,41 +10,48 @@ protocol PlanetVisitor { func visit(planet: PlanetAlderaan) func visit(planet: PlanetCoruscant) func visit(planet: PlanetTatooine) + func visit(planet: MoonJedha) } protocol Planet { func accept(visitor: PlanetVisitor) } -class PlanetAlderaan: Planet { - func accept(visitor: PlanetVisitor) { visitor.visit(self) } +final class MoonJedha: Planet { + func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) } } -class PlanetCoruscant: Planet { - func accept(visitor: PlanetVisitor) { visitor.visit(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) } } -class PlanetTatooine: Planet { - func accept(visitor: PlanetVisitor) { visitor.visit(self) } + +final class PlanetTatooine: Planet { + func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) } } -class NameVisitor: PlanetVisitor { +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" } } + /*: ### Usage */ -let planets: [Planet] = [PlanetAlderaan(), PlanetCoruscant(), PlanetTatooine()] +let planets: [Planet] = [PlanetAlderaan(), PlanetCoruscant(), PlanetTatooine(), MoonJedha()] let names = planets.map { (planet: Planet) -> String in let visitor = NameVisitor() - planet.accept(visitor) - return visitor.name + planet.accept(visitor: visitor) + + return visitor.name } names -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Visitor) -*/ diff --git a/source/contents.md b/source/contents.md new file mode 100644 index 0000000..8503238 --- /dev/null +++ b/source/contents.md @@ -0,0 +1,6 @@ + +## Table of Contents + +* [Behavioral](Behavioral) +* [Creational](Creational) +* [Structural](Structural) diff --git a/source/contentsReadme.md b/source/contentsReadme.md new file mode 100644 index 0000000..6f1d4c6 --- /dev/null +++ b/source/contentsReadme.md @@ -0,0 +1,16 @@ + +## Table of Contents + +| [Behavioral](#behavioral) | [Creational](#creational) | [Structural](#structural) | +| ------------------------------------------------------ | ---------------------------------------- | ---------------------------------------- | +| [🐝 Chain Of Responsibility](#-chain-of-responsibility) | [🌰 Abstract Factory](#-abstract-factory) | [🔌 Adapter](#-adapter) | +| [👫 Command](#-command) | [👷 Builder](#-builder) | [🌉 Bridge](#-bridge) | +| [🎶 Interpreter](#-interpreter) | [🏭 Factory Method](#-factory-method) | [🌿 Composite](#-composite) | +| [🍫 Iterator](#-iterator) | [🔂 Monostate](#-monostate) | [🍧 Decorator](#-decorator) | +| [💐 Mediator](#-mediator) | [🃏 Prototype](#-prototype) | [🎁 Façade](#-fa-ade) | +| [💾 Memento](#-memento) | [💍 Singleton](#-singleton) | [🍃 Flyweight](#-flyweight) | +| [👓 Observer](#-observer) | | [☔ Protection Proxy](#-protection-proxy) | +| [🐉 State](#-state) | | [🍬 Virtual Proxy](#-virtual-proxy) | +| [💡 Strategy](#-strategy) | | | +| [📝 Template Method](#-template-method) | | | +| [🏃 Visitor](#-visitor) | | | diff --git a/source/creational/abstract_factory.swift b/source/creational/abstract_factory.swift index 985240b..16d2899 100644 --- a/source/creational/abstract_factory.swift +++ b/source/creational/abstract_factory.swift @@ -6,65 +6,56 @@ The abstract factory pattern is used to provide a client with a set of related o The "family" of objects created by the factory are determined at run-time. ### Example -*/ -/*: + Protocols */ -protocol Decimal { - func stringValue() -> String - // factory - static func make(string : String) -> Decimal + +protocol BurgerDescribing { + var ingredients: [String] { get } } -typealias NumberFactory = (String) -> Decimal +struct CheeseBurger: BurgerDescribing { + let ingredients: [String] +} -// Number implementations with factory methods +protocol BurgerMaking { + func make() -> BurgerDescribing +} -struct NextStepNumber : Decimal { - private var nextStepNumber : NSNumber +// Number implementations with factory methods - func stringValue() -> String { return nextStepNumber.stringValue } - - // factory - static func make(string : String) -> Decimal { - return NextStepNumber(nextStepNumber:NSNumber(longLong:(string as NSString).longLongValue)) +final class BigKahunaBurger: BurgerMaking { + func make() -> BurgerDescribing { + return CheeseBurger(ingredients: ["Cheese", "Burger", "Lettuce", "Tomato"]) } } -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) +final class JackInTheBox: BurgerMaking { + func make() -> BurgerDescribing { + return CheeseBurger(ingredients: ["Cheese", "Burger", "Tomato", "Onions"]) } } + /*: Abstract factory */ -enum NumberType { - case NextStep, Swift -} -enum NumberHelper { - static func factoryFor(type : NumberType) -> NumberFactory { - switch type { - case .NextStep: - return NextStepNumber.make - case .Swift: - return SwiftNumber.make +enum BurgerFactoryType: BurgerMaking { + + case bigKahuna + case jackInTheBox + + func make() -> BurgerDescribing { + switch self { + case .bigKahuna: + return BigKahunaBurger().make() + case .jackInTheBox: + return JackInTheBox().make() } } } /*: ### Usage */ -let factoryOne = NumberHelper.factoryFor(.NextStep) -let numberOne = factoryOne("1") -numberOne.stringValue() - -let factoryTwo = NumberHelper.factoryFor(.Swift) -let numberTwo = factoryTwo("2") -numberTwo.stringValue() +let bigKahuna = BurgerFactoryType.bigKahuna.make() +let jackInTheBox = BurgerFactoryType.jackInTheBox.make() diff --git a/source/creational/builder.swift b/source/creational/builder.swift index 65bfc87..139139a 100644 --- a/source/creational/builder.swift +++ b/source/creational/builder.swift @@ -7,7 +7,7 @@ An external class controls the construction algorithm. ### Example */ -class DeathStarBuilder { +final class DeathStarBuilder { var x: Double? var y: Double? @@ -28,7 +28,7 @@ struct DeathStar : CustomStringConvertible { init?(builder: DeathStarBuilder) { - if let x = builder.x, y = builder.y, z = builder.z { + if let x = builder.x, let y = builder.y, let z = builder.z { self.x = x self.y = y self.z = z @@ -51,6 +51,3 @@ let empire = DeathStarBuilder { builder in } let deathStar = DeathStar(builder:empire) -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Builder) -*/ diff --git a/source/creational/factory.swift b/source/creational/factory.swift index 94051d1..aeee853 100644 --- a/source/creational/factory.swift +++ b/source/creational/factory.swift @@ -6,42 +6,45 @@ The factory pattern is used to replace class constructors, abstracting the proce ### Example */ -protocol Currency { - func symbol() -> String - func code() -> String +protocol CurrencyDescribing { + var symbol: String { get } + var code: String { get } } -class Euro : Currency { - func symbol() -> String { +final class Euro: CurrencyDescribing { + var symbol: String { return "€" } - func code() -> String { + var code: String { return "EUR" } } -class UnitedStatesDolar : Currency { - func symbol() -> String { +final class UnitedStatesDolar: CurrencyDescribing { + var symbol: String { return "$" } - func code() -> String { + var code: String { return "USD" } } enum Country { - case UnitedStates, Spain, UK, Greece + case unitedStates + case spain + case uk + case greece } enum CurrencyFactory { - static func currencyForCountry(country:Country) -> Currency? { + static func currency(for country: Country) -> CurrencyDescribing? { switch country { - case .Spain, .Greece : + case .spain, .greece: return Euro() - case .UnitedStates : + case .unitedStates: return UnitedStatesDolar() default: return nil @@ -54,7 +57,7 @@ enum CurrencyFactory { */ let noCurrencyCode = "No Currency Code Available" -CurrencyFactory.currencyForCountry(.Greece)?.code() ?? noCurrencyCode -CurrencyFactory.currencyForCountry(.Spain)?.code() ?? noCurrencyCode -CurrencyFactory.currencyForCountry(.UnitedStates)?.code() ?? noCurrencyCode -CurrencyFactory.currencyForCountry(.UK)?.code() ?? noCurrencyCode +CurrencyFactory.currency(for: .greece)?.code ?? noCurrencyCode +CurrencyFactory.currency(for: .spain)?.code ?? noCurrencyCode +CurrencyFactory.currency(for: .unitedStates)?.code ?? noCurrencyCode +CurrencyFactory.currency(for: .uk)?.code ?? noCurrencyCode diff --git a/source/creational/_title.swift b/source/creational/header.md similarity index 80% rename from source/creational/_title.swift rename to source/creational/header.md index 154c33a..1684668 100644 --- a/source/creational/_title.swift +++ b/source/creational/header.md @@ -1,13 +1,7 @@ -//: [Behavioral](Behavioral) | -//: Creational | -//: [Structural](Structural) -/*: + Creational ========== > 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](http://en.wikipedia.org/wiki/Creational_pattern) -*/ -import Swift -import Foundation diff --git a/source/creational/monostate.swift b/source/creational/monostate.swift new file mode 100644 index 0000000..90af5a4 --- /dev/null +++ b/source/creational/monostate.swift @@ -0,0 +1,40 @@ +/*: + 🔂 Monostate + ------------ + + The monostate pattern is another way to achieve singularity. It works through a completely different mechanism, it enforces the behavior of singularity without imposing structural constraints. + So in that case, monostate saves the state as static instead of the entire instance as a singleton. + [SINGLETON and MONOSTATE - Robert C. Martin](http://staff.cs.utu.fi/~jounsmed/doos_06/material/SingletonAndMonostate.pdf) + +### Example: +*/ +class Settings { + + enum Theme { + case `default` + case old + case new + } + + private static var theme: Theme? + + var currentTheme: Theme { + get { Settings.theme ?? .default } + set(newTheme) { Settings.theme = newTheme } + } +} +/*: +### Usage: +*/ + +import SwiftUI + +// When change the theme +let settings = Settings() // Starts using theme .old +settings.currentTheme = .new // Change theme to .new + +// On screen 1 +let screenColor: Color = Settings().currentTheme == .old ? .gray : .white + +// On screen 2 +let screenTitle: String = Settings().currentTheme == .old ? "Itunes Connect" : "App Store Connect" diff --git a/source/creational/prototype.swift b/source/creational/prototype.swift index e19b974..e9ce828 100644 --- a/source/creational/prototype.swift +++ b/source/creational/prototype.swift @@ -7,31 +7,29 @@ This practise is particularly useful when the construction of a new object is in ### Example */ -class ChungasRevengeDisplay { - var name: String? - let font: String +class MoonWorker { - init(font: String) { - self.font = font + let name: String + var health: Int = 100 + + init(name: String) { + self.name = name } - func clone() -> ChungasRevengeDisplay { - return ChungasRevengeDisplay(font:self.font) + func clone() -> MoonWorker { + return MoonWorker(name: name) } } /*: ### Usage */ -let Prototype = ChungasRevengeDisplay(font:"GotanProject") +let prototype = MoonWorker(name: "Sam Bell") -let Philippe = Prototype.clone() -Philippe.name = "Philippe" +var bell1 = prototype.clone() +bell1.health = 12 -let Christoph = Prototype.clone() -Christoph.name = "Christoph" +var bell2 = prototype.clone() +bell2.health = 23 -let Eduardo = Prototype.clone() -Eduardo.name = "Eduardo" -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Prototype) -*/ +var bell3 = prototype.clone() +bell3.health = 0 diff --git a/source/creational/singleton.swift b/source/creational/singleton.swift index 193118a..539bca5 100644 --- a/source/creational/singleton.swift +++ b/source/creational/singleton.swift @@ -8,8 +8,9 @@ There are very few applications, do not overuse this pattern! ### Example: */ -class DeathStarSuperlaser { - static let sharedInstance = DeathStarSuperlaser() +final class ElonMusk { + + static let shared = ElonMusk() private init() { // Private initialization to ensure just one instance is created. @@ -18,4 +19,4 @@ class DeathStarSuperlaser { /*: ### Usage: */ -let laser = DeathStarSuperlaser.sharedInstance +let elon = ElonMusk.shared // There is only one Elon Musk folks. diff --git a/source/endComment b/source/endComment new file mode 100644 index 0000000..9f82b4e --- /dev/null +++ b/source/endComment @@ -0,0 +1,2 @@ + +*/ \ No newline at end of file diff --git a/source/endSwiftCode b/source/endSwiftCode new file mode 100644 index 0000000..7dc72a8 --- /dev/null +++ b/source/endSwiftCode @@ -0,0 +1,2 @@ +``` + diff --git a/source/footer.md b/source/footer.md new file mode 100644 index 0000000..21002da --- /dev/null +++ b/source/footer.md @@ -0,0 +1,5 @@ + +Info +==== + +📖 Descriptions from: [Gang of Four Design Patterns Reference Sheet](http://www.blackwasp.co.uk/GangOfFour.aspx) diff --git a/source/footer.swift b/source/footer.swift deleted file mode 100644 index 8e4983a..0000000 --- a/source/footer.swift +++ /dev/null @@ -1,10 +0,0 @@ -/*: - -Info -==== - -📖 Descriptions from: [Gang of Four Design Patterns Reference Sheet](http://www.blackwasp.co.uk/GangOfFour.aspx) - -🚀 How to generate playground (+zip) from source: [GENERATE.md](https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/GENERATE.md) - -*/ \ No newline at end of file diff --git a/source/header.swift b/source/header.swift deleted file mode 100644 index 94ac3dc..0000000 --- a/source/header.swift +++ /dev/null @@ -1,14 +0,0 @@ - -Design Patterns implemented in Swift 2.2 -======================================== -A short cheat-sheet with Xcode 7.3 Playground ([Design-Patterns.playground.zip](https://raw.githubusercontent.com/ochococo/Design-Patterns-In-Swift/master/Design-Patterns.playground.zip)). - -👷 Project maintained by: [@nsmeme](http://twitter.com/nsmeme) (Oktawian Chojnacki) - -## Table of Contents - -* [Behavioral](#behavioral) -* [Creational](#creational) -* [Structural](#structural) - -*/ \ No newline at end of file diff --git a/source/imports.swift b/source/imports.swift new file mode 100644 index 0000000..350c82f --- /dev/null +++ b/source/imports.swift @@ -0,0 +1,2 @@ + +import Foundation diff --git a/source/startComment b/source/startComment new file mode 100644 index 0000000..a23f02a --- /dev/null +++ b/source/startComment @@ -0,0 +1 @@ +/*: diff --git a/source/startSwiftCode b/source/startSwiftCode new file mode 100644 index 0000000..591b2be --- /dev/null +++ b/source/startSwiftCode @@ -0,0 +1,3 @@ + + +```swift \ No newline at end of file diff --git a/source/structural/adapter.swift b/source/structural/adapter.swift index 5e21a7d..3a99081 100644 --- a/source/structural/adapter.swift +++ b/source/structural/adapter.swift @@ -6,18 +6,18 @@ The adapter pattern is used to provide a link between two otherwise incompatible ### Example */ -protocol OlderDeathStarSuperLaserAiming { - var angleV: NSNumber {get} - var angleH: NSNumber {get} +protocol NewDeathStarSuperLaserAiming { + var angleV: Double { get } + var angleH: Double { get } } /*: **Adaptee** */ -struct DeathStarSuperlaserTarget { - let angleHorizontal: Double - let angleVertical: Double +struct OldDeathStarSuperlaserTarget { + let angleHorizontal: Float + let angleVertical: Float - init(angleHorizontal:Double, angleVertical:Double) { + init(angleHorizontal: Float, angleVertical: Float) { self.angleHorizontal = angleHorizontal self.angleVertical = angleVertical } @@ -25,29 +25,27 @@ struct DeathStarSuperlaserTarget { /*: **Adapter** */ -struct OldDeathStarSuperlaserTarget : OlderDeathStarSuperLaserAiming { - private let target : DeathStarSuperlaserTarget +struct NewDeathStarSuperlaserTarget: NewDeathStarSuperLaserAiming { - var angleV:NSNumber { - return NSNumber(double: target.angleVertical) + private let target: OldDeathStarSuperlaserTarget + + var angleV: Double { + return Double(target.angleVertical) } - var angleH:NSNumber { - return NSNumber(double: target.angleHorizontal) + var angleH: Double { + return Double(target.angleHorizontal) } - init(_ target:DeathStarSuperlaserTarget) { + init(_ target: OldDeathStarSuperlaserTarget) { self.target = target } } /*: ### Usage */ -let target = DeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) -let oldFormat = OldDeathStarSuperlaserTarget(target) +let target = OldDeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) +let newFormat = NewDeathStarSuperlaserTarget(target) -oldFormat.angleH -oldFormat.angleV -/*: ->**Further Examples:** [Design Patterns in Swift](https://github.com/kingreza/Swift-Adapter) -*/ +newFormat.angleH +newFormat.angleV diff --git a/source/structural/bridge.swift b/source/structural/bridge.swift index e5972d6..2384c7e 100644 --- a/source/structural/bridge.swift +++ b/source/structural/bridge.swift @@ -7,7 +7,7 @@ The bridge pattern is used to separate the abstract elements of a class from the ### Example */ protocol Switch { - var appliance: Appliance {get set} + var appliance: Appliance { get set } func turnOn() } @@ -15,7 +15,7 @@ protocol Appliance { func run() } -class RemoteControl: Switch { +final class RemoteControl: Switch { var appliance: Appliance func turnOn() { @@ -27,13 +27,13 @@ class RemoteControl: Switch { } } -class TV: Appliance { +final class TV: Appliance { func run() { print("tv turned on"); } } -class VacuumCleaner: Appliance { +final class VacuumCleaner: Appliance { func run() { print("vacuum cleaner turned on") } @@ -41,8 +41,8 @@ class VacuumCleaner: Appliance { /*: ### Usage */ -var tvRemoteControl = RemoteControl(appliance: TV()) +let tvRemoteControl = RemoteControl(appliance: TV()) tvRemoteControl.turnOn() -var fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner()) +let fancyVacuumCleanerRemoteControl = RemoteControl(appliance: VacuumCleaner()) fancyVacuumCleanerRemoteControl.turnOn() diff --git a/source/structural/composite.swift b/source/structural/composite.swift index 1c5f134..522cd7c 100644 --- a/source/structural/composite.swift +++ b/source/structural/composite.swift @@ -5,23 +5,22 @@ 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. ### Example -*/ -/*: + Component */ protocol Shape { func draw(fillColor: String) } -/*: +/*: Leafs -*/ -class Square : Shape { +*/ +final class Square: Shape { func draw(fillColor: String) { print("Drawing a Square with color \(fillColor)") } } -class Circle : Shape { +final class Circle: Shape { func draw(fillColor: String) { print("Drawing a circle with color \(fillColor)") } @@ -30,16 +29,17 @@ class Circle : Shape { /*: Composite */ -class Whiteboard : Shape { - lazy var shapes = [Shape]() - - init(_ shapes:Shape...) { +final class Whiteboard: Shape { + + private lazy var shapes = [Shape]() + + init(_ shapes: Shape...) { self.shapes = shapes } - - func draw(fillColor:String) { + + func draw(fillColor: String) { for shape in self.shapes { - shape.draw(fillColor) + shape.draw(fillColor: fillColor) } } } @@ -47,4 +47,4 @@ class Whiteboard : Shape { ### Usage: */ var whiteboard = Whiteboard(Circle(), Square()) -whiteboard.draw("Red") +whiteboard.draw(fillColor: "Red") diff --git a/source/structural/decorator.swift b/source/structural/decorator.swift index 413a191..c531d1e 100644 --- a/source/structural/decorator.swift +++ b/source/structural/decorator.swift @@ -7,71 +7,56 @@ This provides a flexible alternative to using inheritance to modify behaviour. ### Example */ -protocol Coffee { - func getCost() -> Double - func getIngredients() -> String +protocol CostHaving { + var cost: Double { get } } -class SimpleCoffee: Coffee { - func getCost() -> Double { - return 1.0 - } - - func getIngredients() -> String { - return "Coffee" - } +protocol IngredientsHaving { + var ingredients: [String] { get } } -class CoffeeDecorator: Coffee { - private let decoratedCoffee: Coffee - private let ingredientSeparator: String = ", " +typealias BeverageDataHaving = CostHaving & IngredientsHaving - required init(decoratedCoffee: Coffee) { - self.decoratedCoffee = decoratedCoffee - } - - func getCost() -> Double { - return decoratedCoffee.getCost() - } +struct SimpleCoffee: BeverageDataHaving { + let cost: Double = 1.0 + let ingredients = ["Water", "Coffee"] +} - func getIngredients() -> String { - return decoratedCoffee.getIngredients() - } +protocol BeverageHaving: BeverageDataHaving { + var beverage: BeverageDataHaving { get } } -class Milk: CoffeeDecorator { - required init(decoratedCoffee: Coffee) { - super.init(decoratedCoffee: decoratedCoffee) - } +struct Milk: BeverageHaving { - override func getCost() -> Double { - return super.getCost() + 0.5 + let beverage: BeverageDataHaving + + var cost: Double { + return beverage.cost + 0.5 } - override func getIngredients() -> String { - return super.getIngredients() + ingredientSeparator + "Milk" + var ingredients: [String] { + return beverage.ingredients + ["Milk"] } } -class WhipCoffee: CoffeeDecorator { - required init(decoratedCoffee: Coffee) { - super.init(decoratedCoffee: decoratedCoffee) - } +struct WhipCoffee: BeverageHaving { + + let beverage: BeverageDataHaving - override func getCost() -> Double { - return super.getCost() + 0.7 + var cost: Double { + return beverage.cost + 0.5 } - override func getIngredients() -> String { - return super.getIngredients() + ingredientSeparator + "Whip" + var ingredients: [String] { + return beverage.ingredients + ["Whip"] } } /*: ### Usage: */ -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())") +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)") diff --git a/source/structural/facade.swift b/source/structural/facade.swift index 25787f6..1147d57 100644 --- a/source/structural/facade.swift +++ b/source/structural/facade.swift @@ -6,23 +6,31 @@ The facade pattern is used to define a simplified interface to a more complex su ### Example */ -enum Eternal { +final class Defaults { - static func setObject(value: AnyObject!, forKey defaultName: String!) { - let defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults() - defaults.setObject(value, forKey:defaultName) - defaults.synchronize() + private let defaults: UserDefaults + + init(defaults: UserDefaults = .standard) { + self.defaults = defaults } - static func objectForKey(defaultName: String!) -> AnyObject! { - let defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults() + subscript(key: String) -> String? { + get { + return defaults.string(forKey: key) + } - return defaults.objectForKey(defaultName) + set { + defaults.set(newValue, forKey: key) + } } - } /*: ### Usage */ -Eternal.setObject("Disconnect me. I’d rather be nothing", forKey:"Bishop") -Eternal.objectForKey("Bishop") +let storage = Defaults() + +// Store +storage["Bishop"] = "Disconnect me. I’d rather be nothing" + +// Read +storage["Bishop"] diff --git a/source/structural/flyweight.swift b/source/structural/flyweight.swift new file mode 100644 index 0000000..3a2d944 --- /dev/null +++ b/source/structural/flyweight.swift @@ -0,0 +1,55 @@ +/*: +## 🍃 Flyweight +The flyweight pattern is used to minimize memory usage or computational expenses by sharing as much as possible with other similar objects. +### Example +*/ +// Instances of SpecialityCoffee will be the Flyweights +struct SpecialityCoffee { + let origin: String +} + +protocol CoffeeSearching { + func search(origin: String) -> SpecialityCoffee? +} + +// Menu acts as a factory and cache for SpecialityCoffee flyweight objects +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)") + } + } +} +/*: +### Usage +*/ +let coffeeShop = CoffeeShop(menu: Menu()) + +coffeeShop.takeOrder(origin: "Yirgacheffe, Ethiopia", table: 1) +coffeeShop.takeOrder(origin: "Buziraguhindwa, Burundi", table: 3) + +coffeeShop.serve() diff --git a/source/structural/_title.swift b/source/structural/header.md similarity index 69% rename from source/structural/_title.swift rename to source/structural/header.md index a944c72..67cdc66 100644 --- a/source/structural/_title.swift +++ b/source/structural/header.md @@ -1,13 +1,7 @@ -//: [Behavioral](Behavioral) | -//: [Creational](Creational) | -//: Structural -/*: + 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](http://en.wikipedia.org/wiki/Structural_pattern) -*/ -import Swift -import Foundation diff --git a/source/structural/protection_proxy.swift b/source/structural/protection_proxy.swift index d20c1c4..f012f47 100644 --- a/source/structural/protection_proxy.swift +++ b/source/structural/protection_proxy.swift @@ -7,23 +7,23 @@ Protection proxy is restricting access. ### Example */ -protocol DoorOperator { - func openDoors(doors: String) -> String +protocol DoorOpening { + func open(doors: String) -> String } -class HAL9000 : DoorOperator { - func openDoors(doors: String) -> String { +final class HAL9000: DoorOpening { + func open(doors: String) -> String { return ("HAL9000: Affirmative, Dave. I read you. Opened \(doors).") } } -class CurrentComputer : DoorOperator { +final class CurrentComputer: DoorOpening { private var computer: HAL9000! - func authenticateWithPassword(pass: String) -> Bool { + func authenticate(password: String) -> Bool { - guard pass == "pass" else { - return false; + guard password == "pass" else { + return false } computer = HAL9000() @@ -31,22 +31,22 @@ class CurrentComputer : DoorOperator { return true } - func openDoors(doors: String) -> String { + func open(doors: String) -> String { guard computer != nil else { return "Access Denied. I'm afraid I can't do that." } - return computer.openDoors(doors) + return computer.open(doors: doors) } } /*: ### Usage */ let computer = CurrentComputer() -let doors = "Pod Bay Doors" +let podBay = "Pod Bay Doors" -computer.openDoors(doors) +computer.open(doors: podBay) -computer.authenticateWithPassword("pass") -computer.openDoors(doors) +computer.authenticate(password: "pass") +computer.open(doors: podBay) diff --git a/source/structural/virtual_proxy.swift b/source/structural/virtual_proxy.swift index ca11ef8..9a02414 100644 --- a/source/structural/virtual_proxy.swift +++ b/source/structural/virtual_proxy.swift @@ -2,7 +2,7 @@ 🍬 Virtual Proxy ---------------- -The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. +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. ### Example @@ -11,13 +11,14 @@ protocol HEVSuitMedicalAid { func administerMorphine() -> String } -class HEVSuit : HEVSuitMedicalAid { +final class HEVSuit: HEVSuitMedicalAid { func administerMorphine() -> String { - return "Morphine aministered." + return "Morphine administered." } } -class HEVSuitHumanInterface : HEVSuitMedicalAid { +final class HEVSuitHumanInterface: HEVSuitMedicalAid { + lazy private var physicalSuit: HEVSuit = HEVSuit() func administerMorphine() -> String {