設計模式 Swift 實踐 - (Part 1. 行爲型模式)

行爲型模式 | 建立型模式 | 結構型模式javascript

行爲型模式

在軟件工程中, 行爲型模式爲設計模式的一種類型,用來識別對象之間的經常使用交流模式並加以實現。如此,可在進行這些交流活動時加強彈性。java

來源: 維基百科git

責任鏈(Chain Of Responsibility)

責任鏈模式在面向對象程式設計裏是一種軟件設計模式,它包含了一些命令對象和一系列的處理對象。每個處理對象決定它能處理哪些命令對象,它也知道如何將它不能處理的命令對象傳遞給該鏈中的下一個處理對象。github

示例:

final class MoneyPile {

    let value: Int
    var quantity: Int
    var nextPile: MoneyPile?

    init(value: Int, quantity: Int, nextPile: MoneyPile?) {
        self.value = value
        self.quantity = quantity
        self.nextPile = nextPile
    }

    func canWithdraw(amount: Int) -> Bool {

        var amount = amount

        func canTakeSomeBill(want: Int) -> Bool {
            return (want / self.value) > 0
        }

        var quantity = self.quantity

        while canTakeSomeBill(want: amount) {

            if quantity == 0 {
                break
            }

            amount -= self.value
            quantity -= 1
        }

        guard amount > 0 else {
            return true
        }

        if let next = self.nextPile {
            return next.canWithdraw(amount: amount)
        }

        return false
    }
}

final class ATM {
    private var hundred: MoneyPile
    private var fifty: MoneyPile
    private var twenty: MoneyPile
    private var ten: MoneyPile

    private var startPile: MoneyPile {
        return self.hundred
    }

    init(hundred: MoneyPile,
         fifty: MoneyPile,
         twenty: MoneyPile,
         ten: MoneyPile) {

        self.hundred = hundred
        self.fifty = fifty
        self.twenty = twenty
        self.ten = ten
    }

    func canWithdraw(amount: Int) -> String {
        return "可否取現:\(self.startPile.canWithdraw(amount: amount))"
    }
}複製代碼

用法:

// 建立一系列的錢堆,並將其連接起來: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)

// 建立 ATM 實例
var atm = ATM(hundred: hundred, fifty: fifty, twenty: twenty, ten: ten)
atm.canWithdraw(amount: 310)
atm.canWithdraw(amount: 100)
atm.canWithdraw(amount: 165)
atm.canWithdraw(amount: 30)複製代碼

更多示例:Design Patterns in Swift算法

命令(Command)

命令模式是一種設計模式,它嘗試以對象來表明實際行動。命令對象能夠把行動(action) 及其參數封裝起來,因而這些行動能夠被:express

  • 重複屢次
  • 取消(若是該對象有實現的話)
  • 取消後又再重作

示例:

protocol DoorCommand {
    func execute() -> String
}

class OpenCommand: DoorCommand {
    let doors: String

    required init(doors: String) {
        self.doors = doors
    }

    func execute() -> String {
        return "\(doors)打開了"
    }
}

class CloseCommand: DoorCommand {
    let doors: String

    required init(doors: String) {
        self.doors = doors
    }

    func execute() -> String {
        return "\(doors)關閉了"
    }
}

class ZhimaDoorsOperations {
    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 zhimaDoors = "芝麻門"
let doorModule = ZhimaDoorsOperations(doors: zhimaDoors)

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)
    }
}複製代碼

用法:

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))

context.assign(expression: a, value: 2)
context.assign(expression: b, value: 1)
context.assign(expression: c, value: 3)

var result = expression.evaluate(context)複製代碼

更多示例:Design Patterns in Swift設計模式

迭代器(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:"紅樓夢")])
for novella in greatNovellas {
    print("我讀了\(novella.name)")
}複製代碼

中介者(Mediator)

用一箇中介者對象封裝一系列的對象交互,中介者使各對象不須要顯示地相互做用,從而使耦合鬆散,並且能夠獨立地改變它們之間的交互。數據結構

示例:

struct Programmer {
    let name: String

    init(name: String) {
        self.name = name
    }

    func receive(message: String) {
        print("\(name) 收到消息:\(message)")
    }
}

protocol MessageSending {
    func send(message: String)
}

final class MessageMediator: MessageSending {

    private 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: MessageSending) {
    worker.send(message: message)
}

let messagesMediator = MessageMediator()

let user0 = Programmer(name: "Linus Torvalds")
let user1 = Programmer(name: "Dylan Wang")
messagesMediator.add(recipient: user0)
messagesMediator.add(recipient: user1)

spamMonster(message: "我但願添加您到個人職業網絡", worker: messagesMediator)複製代碼

更多示例:Design Patterns in Swift

備忘錄(Memento)

在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。這樣就能夠將該對象恢復到原先保存的狀態

示例:

typealias Memento = NSDictionary
// 發起人(Originator)
protocol MementoConvertible {
    var memento: Memento { get }
    init?(memento: Memento)
}

struct GameState: MementoConvertible {

    private struct Keys {
        static let chapter = "com.valve.halflife.chapter"
        static let weapon = "com.valve.halflife.weapon"
    }

    var chapter: String
    var weapon: String

    init(chapter: String, weapon: String) {
        self.chapter = chapter
        self.weapon = chapter
    }

    init?(memento: Memento) {
        guard let mementoChapter = memento[Keys.chapter] as? String,
        let mementoWeapon = memento[Keys.weapon] as? String else {
            return nil
        }

        self.chapter = mementoChapter
        self.weapon = mementoWeapon
    }

    var memento: Memento {
        return [Keys.chapter: chapter,
                Keys.weapon: weapon]
    }
}
// 管理者(Caretaker)
enum CheckPoint {
    static func save(_ state: MementoConvertible, saveName: String) {
        let defaults = UserDefaults.standard
        defaults.set(state.memento, forKey: saveName)
        defaults.synchronize()
    }

    static func restore(saveName: String) -> Memento? {
        let defaults = UserDefaults.standard
        return defaults.object(forKey: saveName) as? Memento
    }
}複製代碼

用法:

var gameState = GameState(chapter: "Black Mesa Inbound", weapon: "Crowbar")

gameState.chapter = "Anomalous Materials"
gameState.weapon = "Glock 17"
CheckPoint.save(gameState, saveName: "gameState1")

gameState.chapter = "Unforeseen Consequences"
gameState.weapon = "MP5"
CheckPoint.save(gameState, saveName: "gameState2")

gameState.chapter = "Office Complex"
gameState.weapon = "Crossbow"
CheckPoint.save(gameState, saveName: "gameState3")

if let memento = CheckPoint.restore(saveName: "gameState1") {
    let finalState = GameState(memento: memento)
    dump(finalState)
}複製代碼

觀察者(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 observer = Observer()
var testChambers = TestChambers()
testChambers.observer = observer
testChambers.testChamberNumber += 1複製代碼

更多示例:Design Patterns in Swift

狀態(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)複製代碼

更多示例:Design Patterns in Swift

策略(Strategy)

對象有某個行爲,可是在不一樣的場景中,該行爲有不一樣的實現算法。策略模式:

  • 定義了一族算法(業務規則);
  • 封裝了每一個算法;
  • 這族的算法可互換代替(interchangeable)。

示例:

protocol PrintStrategy {
    func print(_ string: String) -> String
}

final class Printer {

    private let strategy: PrintStrategy

    init(strategy: PrintStrategy) {
        self.strategy = strategy
    }

    func print(_ string: String) -> String {
        return self.strategy.print(string)
    }
}

final class UpperCaseStrategy: PrintStrategy {
    func print(_ string: String) -> String {
        return string.uppercased()
    }
}

final class LowerCaseStrategy: PrintStrategy {
    func print(_ string: String) -> String {
        return string.lowercased()
    }
}複製代碼

用法

var lower = Printer(strategy: LowerCaseStrategy())
lower.print("0 tempora, o mores")

var upper = Printer(strategy: UpperCaseStrategy())
upper.print("0 tempora, o mores")複製代碼

更多示例:Design Patterns in Swift

訪問者(Visitor)

封裝某些做用於某種數據結構中各元素的操做,它能夠在不改變數據結構的前提下定義做用於這些元素的新的操做。

示例:

protocol PlanetVisitor {
    func visit(planet: PlanetAlderaan)
    func visit(planet: PlanetCoruscant)
    func visit(planet: PlanetTatooine)
    func visit(planet: MoonJedah)
}

protocol Planet {
    func accept(visitor: PlanetVisitor)
}

class MoonJedah: Planet {
    func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}

class PlanetAlderaan: Planet {
    func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}

class PlanetCoruscant: Planet {
    func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}

class PlanetTatooine: Planet {
    func accept(visitor: PlanetVisitor) { visitor.visit(planet: self) }
}

class NameVisitor: PlanetVisitor {
    var name = ""

    func visit(planet: PlanetAlderaan)  { name = "Alderaan" }
    func visit(planet: PlanetCoruscant) { name = "Coruscant" }
    func visit(planet: PlanetTatooine)  { name = "Tatooine" }
    func visit(planet: MoonJedah)         { name = "Jedah" }
}複製代碼

用法:

let planets: [Planet] = [PlanetAlderaan(), PlanetCoruscant(), PlanetTatooine(), MoonJedah()]

let names = planets.map { (planet: Planet) -> String in
    let visitor = NameVisitor()
    planet.accept(visitor: visitor)
    return visitor.name
}

names複製代碼

更多示例:Design Patterns in Swift

接下來:

查看 Playground 版本

相關文章
相關標籤/搜索