設計模式筆記及Swift上的實現之二  『BUILDER(生成器)』

生成器抽象工廠 一樣同屬于創建型模式。

介紹

意圖

將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。

適用性

  • 當創建復雜對象的算法應該獨立于該對象的組成部分已經它們的裝配方式時。
  • 當構造過程必須允許被構造的對象有不同的表示時。

結構

生成器結構.png

參與者

  • Builder

為創建一個 Product 對象的各個部件指定抽象接口。

  • ConcreteBuilder

實現 Builder 的接口已構造和裝配該產品的各個部件。
定義并明確它所創建的表示。
提供一個檢索產品的接口。

  • Director

構造一個 Builder 接口對象。

  • Product

協助

生成器協助.png
  • 客戶創建 Director 對象,比通過他想要的 Builder 對象進行配置。
  • 一旦產品部件被生成,向導器就會通知生成器。
  • 客戶從生成器中檢索產品。

效果

  • 它使你可以改變一個產品的內部表示

Builder 對象提供給導向器一個構造產品的抽象接口。該接口使得生成器可以隱藏產品的表示和內部結構。

  • 它將構造代碼和表示代碼分開
  • 他可以對構造過程進行更精細的控制

Builder 模式與一下子就生成產品的創建型模式不同,它是在導向器控制下一步一步構造產品的。

實現

  • 裝配和構造接口
  • 為什么產品沒有抽象類

由于具體生成器生成的產品,它們的表示相差很大,以至于給不同的產品公共的父類并不現實。

  • 在 Builder 中缺省的方法為空

示例

定義一個生成器 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. 創建迷宮接口
  2. 創建房間接口
  3. 創建門的接口

MazeBuilder 并不用來構建迷宮,它主要用來定義接口。

現在我們定義一個用來構建迷宮的類 StandarMazeBuilder
這里 StandarMazeBuilder 我選擇使用 class, 因為生成器在協助中有一點 客戶從生成器中檢索產品。也就是說用戶使用生成器的方法構造產品后,還可以使用通過現在生成器對象進行檢索產品。這樣使用引用類型是更好的選擇。

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 是個功能性操作,決定兩個房間的公共墻壁的方位(為了簡單處理我這里就固定返回一個值吧)

最后我們需要定義一個導向器。

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

現在再看看如何使用這個生成器來構造迷宮。

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

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

那接下來我們就來完成另外一個需求,我們不去構建迷宮,只對迷宮的構件進行計數。
這個看起來和原來用例構建迷宮的代碼有則很大的差異。

但我們使用生成器模式就不用懼怕這樣的差異。我們重新定義一個 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)
    }
    
}

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

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

最后來點總結

相比抽象工廠模式,生成器模式更擅長于對內部表示存在較大差異的產品定義統一的接口。使得構建代碼和表示代碼分離。

最最后 歡迎討論、批評、指錯

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容

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