設(shè)計(jì)模式筆記及Swift上的實(shí)現(xiàn)之二  『BUILDER(生成器)』

生成器抽象工廠 一樣同屬于創(chuàng)建型模式。

介紹

意圖

將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。

適用性

  • 當(dāng)創(chuàng)建復(fù)雜對(duì)象的算法應(yīng)該獨(dú)立于該對(duì)象的組成部分已經(jīng)它們的裝配方式時(shí)。
  • 當(dāng)構(gòu)造過程必須允許被構(gòu)造的對(duì)象有不同的表示時(shí)。

結(jié)構(gòu)

生成器結(jié)構(gòu).png

參與者

  • Builder

為創(chuàng)建一個(gè) Product 對(duì)象的各個(gè)部件指定抽象接口。

  • ConcreteBuilder

實(shí)現(xiàn) Builder 的接口已構(gòu)造和裝配該產(chǎn)品的各個(gè)部件。
定義并明確它所創(chuàng)建的表示。
提供一個(gè)檢索產(chǎn)品的接口。

  • Director

構(gòu)造一個(gè) Builder 接口對(duì)象。

  • Product

協(xié)助

生成器協(xié)助.png
  • 客戶創(chuàng)建 Director 對(duì)象,比通過他想要的 Builder 對(duì)象進(jìn)行配置。
  • 一旦產(chǎn)品部件被生成,向?qū)骶蜁?huì)通知生成器。
  • 客戶從生成器中檢索產(chǎn)品。

效果

  • 它使你可以改變一個(gè)產(chǎn)品的內(nèi)部表示

Builder 對(duì)象提供給導(dǎo)向器一個(gè)構(gòu)造產(chǎn)品的抽象接口。該接口使得生成器可以隱藏產(chǎn)品的表示和內(nèi)部結(jié)構(gòu)。

  • 它將構(gòu)造代碼和表示代碼分開
  • 他可以對(duì)構(gòu)造過程進(jìn)行更精細(xì)的控制

Builder 模式與一下子就生成產(chǎn)品的創(chuàng)建型模式不同,它是在導(dǎo)向器控制下一步一步構(gòu)造產(chǎn)品的。

實(shí)現(xiàn)

  • 裝配和構(gòu)造接口
  • 為什么產(chǎn)品沒有抽象類

由于具體生成器生成的產(chǎn)品,它們的表示相差很大,以至于給不同的產(chǎn)品公共的父類并不現(xiàn)實(shí)。

  • 在 Builder 中缺省的方法為空

示例

定義一個(gè)生成器 MazeBuilder

protocol MazeBuilder {
    
    func buildMaze()
    func buildRoom(n: Int)
    func buildDoor(roomFrom: Int, roomTo: Int)
    
    func getMaze() -> Maze?
    
}

extension MazeBuilder {
    
    func buildMaze() {}
    func buildRoom(n: Int) {}
    func buildDoor(roomFrom: Int, roomTo: Int) { }
    
    func getMaze() -> Maze? { return nil }
    
}

主要接口有:

  1. 創(chuàng)建迷宮接口
  2. 創(chuàng)建房間接口
  3. 創(chuàng)建門的接口

MazeBuilder 并不用來構(gòu)建迷宮,它主要用來定義接口。

現(xiàn)在我們定義一個(gè)用來構(gòu)建迷宮的類 StandarMazeBuilder
這里 StandarMazeBuilder 我選擇使用 class, 因?yàn)樯善髟趨f(xié)助中有一點(diǎn) 客戶從生成器中檢索產(chǎn)品。也就是說用戶使用生成器的方法構(gòu)造產(chǎn)品后,還可以使用通過現(xiàn)在生成器對(duì)象進(jìn)行檢索產(chǎn)品。這樣使用引用類型是更好的選擇。

class StandarMazeBuilder: MazeBuilder {
    
    var currentMaze: Maze?
    
    func buildMaze() {
        currentMaze = Maze()
    }
    
    func buildRoom(n: Int) {
        
        var room = Room(no: n)
        
        room.setSide(dect: .east, site: Wall())
        room.setSide(dect: .north, site: Wall())
        room.setSide(dect: .south, site: Wall())
        room.setSide(dect: .west, site: Wall())
        
        currentMaze?.addRoom(room: room)
        
    }
    
    func commonWall(r1: Room, r2: Room) -> Direction {
        return .north
    }
    
    func buildDoor(roomFrom: Int, roomTo: Int) {
        
        var r1 = currentMaze!.getRoom(roomFrom)
        
        var r2 = currentMaze!.getRoom(roomTo)
        
        let d = Door(r1: r1, r2: r2)
        
        r1.setSide(dect: commonWall(r1: r1, r2: r2), site: d)
        r2.setSide(dect: commonWall(r1: r2, r2: r1), site: d)
        
    }
    
    func getMaze() -> Maze? {
        return currentMaze
    }
    
}

這里 commonWall 是個(gè)功能性操作,決定兩個(gè)房間的公共墻壁的方位(為了簡單處理我這里就固定返回一個(gè)值吧)

最后我們需要定義一個(gè)導(dǎo)向器。

struct MazeGame {
    
    func createMaze(builder: MazeBuilder) -> Maze? {
        
        builder.buildMaze()
        
        builder.buildRoom(n: 1)
        builder.buildRoom(n: 2)
        
        builder.buildDoor(roomFrom: 1, roomTo: 2)
        
        return builder.getMaze()
        
    }
    
}

現(xiàn)在再看看如何使用這個(gè)生成器來構(gòu)造迷宮。

let game = MazeGame()

let builder = StandarMazeBuilder()

game.createMaze(builder: builder)

let maze = builder.getMaze()

print("\(maze!)")

打印信息:

===========================
Maze room:
room_2 Room 
north is Optional(Wall)
south is Optional(Wall)
east is Optional(Wall)
west is Optional(Wall)
room_1 Room 
north is Optional(Wall)
south is Optional(Wall)
east is Optional(Wall)
west is Optional(Wall)
===========================

到現(xiàn)在生成器模式看上去好像沒什么特別之處。

那接下來我們就來完成另外一個(gè)需求,我們不去構(gòu)建迷宮,只對(duì)迷宮的構(gòu)件進(jìn)行計(jì)數(shù)。
這個(gè)看起來和原來用例構(gòu)建迷宮的代碼有則很大的差異。

但我們使用生成器模式就不用懼怕這樣的差異。我們重新定義一個(gè) CountingMazeBuilder 生成器就可以了。

class CountingMazeBuilder: MazeBuilder {
    
    
    var doors = 0
    var rooms = 0;
    
    func buildDoor(roomFrom: Int, roomTo: Int) {
        doors += 1
    }
    
    func buildRoom(n: Int) {
        rooms += 1
    }
    
    func getCounts() -> (r: Int, d: Int) {
        return (rooms, doors)
    }
    
}

我們再看看用戶如何使用這個(gè)生成器,代碼視乎沒有太大的變化。

let game = MazeGame()

let countingBuilder = CountingMazeBuilder()

game.createMaze(builder: countingBuilder)

let count = countingBuilder.getCounts()

print("The maze has \nrooms \(count.r) \ndoors \(count.d)")

打印信息:

The maze has 
rooms 2 
doors 1

最后來點(diǎn)總結(jié)

相比抽象工廠模式,生成器模式更擅長于對(duì)內(nèi)部表示存在較大差異的產(chǎn)品定義統(tǒng)一的接口。使得構(gòu)建代碼和表示代碼分離。

最最后 歡迎討論、批評(píng)、指錯(cuò)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 場景問題# 1.1 繼續(xù)導(dǎo)出數(shù)據(jù)的應(yīng)用框架## 在討論工廠方法模式的時(shí)候,提到了一個(gè)導(dǎo)出數(shù)據(jù)的應(yīng)用框架。 對(duì)于...
    七寸知架構(gòu)閱讀 6,170評(píng)論 1 64
  • 前段時(shí)間,在自己糊里糊涂地寫了一年多的代碼之后,接手了一坨一個(gè)同事的代碼。身邊很多人包括我自己都在痛罵那些亂糟糟毫...
    丑小丫大笨蛋閱讀 685評(píng)論 0 2
  • 沒有人買車會(huì)只買一個(gè)輪胎或者方向盤,大家買的都是一輛包含輪胎、方向盤和發(fā)動(dòng)機(jī)等多個(gè)部件的完整汽車。如何將這些部件組...
    justCode_閱讀 2,009評(píng)論 1 6
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,699評(píng)論 19 139
  • 本文是《設(shè)計(jì)模式——可復(fù)用面對(duì)對(duì)象軟件的基礎(chǔ)》的筆記。 面對(duì)對(duì)象設(shè)計(jì)的幾個(gè)原則:1.針對(duì)接口編程,而不是針對(duì)實(shí)現(xiàn)編...
    Lension閱讀 1,378評(píng)論 0 0

友情鏈接更多精彩內(nèi)容