<section id="nice" data-tool="mdnice編輯器" data-website="https://www.mdnice.com" style="font-size: 16px; color: black; padding: 0 10px; line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;"><h1 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 24px;"><span class="prefix" style="display: none;"></span><span class="content">Builder(生成器)</span><span class="suffix"></span></h1>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">Builder(生成器)屬於建立型模式,針對的是單個複雜對象的建立。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">意圖:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。</p>
<h2 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px;"><span class="prefix" style="display: none;"></span><span class="content">舉例子</span><span class="suffix"></span></h2>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">若是看不懂上面的意圖介紹,沒有關係,設計模式須要在平常工做裏用起來,結合例子能夠加深你的理解,下面我準備了三個例子,讓你體會什麼場景下會用到這種設計模式。</p>
<h3 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">搭樂高積木</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">樂高積木是很典型的隨機拼裝場景,你有不少樂高積木,要搭一個小房子都太複雜了,可能不得不看着說明書一步步操做,這就像建立一個複雜的對象,要傳入很是多的參數,並且順序還不能錯。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">若是不考慮拼裝樂高過程當中的樂趣,你只是想快速獲得一個標準的房子,怎麼樣才能夠最快最省事?</p>
<h3 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">工廠流水線</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">製做一個罐頭要經歷許多步驟,而其中一些步驟好比製做罐頭是通用的,能夠用這個罐頭裝不少東西,好比紅棗罐頭、黃桃罐頭,那工廠流水線是怎麼作到靈活可拓展的呢?</p>
<h3 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">建立數據庫鏈接池</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">創建一個數據庫鏈接池,咱們須要傳入數據庫的地址、用戶名與密碼、還有要建立多少大小的鏈接池,緩存的位置等等。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">考慮到數據庫必須正確鏈接後纔有效,建立時必須校驗傳入的數據庫地址與密碼的正確性,甚至存儲方式與數據庫類型還有關係,這是一個簡單的 new
實例化能夠解決的嗎?</p>
<h2 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px;"><span class="prefix" style="display: none;"></span><span class="content">意圖解釋</span><span class="suffix"></span></h2>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">在樂高積木的例子中,咱們爲了獲得一個房子其實不須要關心每個積木應該如何擺放,咱們只要交給組裝工廠(一我的或者一個程序)產出標準房子就好了,這其中參數多是 .setHouseType().build()
設置房屋類型,而不須要 new House(block1, block2, ... block999)
傳遞這些不必的參數。其中組裝工廠就是生成器。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">在工廠流水線的例子中,流水線就是生成器,一個流水線能夠不經過不一樣組合生成不一樣做用的工廠,黃桃罐頭的流水線能夠理解爲 new Builder().組裝罐頭().放入黃桃().build()
,紅棗罐頭的流水線能夠理解爲 new Builder().組裝罐頭().放入紅棗().build()
,咱們能夠複用生成器最基礎的函數 組裝罐頭()
將其用於建立不一樣的產品中,複用了組裝基礎能力。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">在建立數據庫例子中,咱們能夠先設置一些必要的參數再建立,好比 new Builder().setUrl().setPassword().setType().build()
,這樣在最終執行 build
函數的時候,能夠對參數中存在關聯的進行校驗,而獲得的對象也沒法再被修改,這樣比直接暴露數據庫鏈接池對象,再一個值一個值 Set 多了以下好處:</p>
<ol data-tool="mdnice編輯器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: decimal;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">對象沒法被修改,保護了程序穩定性,減小了維護複雜度。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">能夠對參數關聯進行一次性校驗。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">在建立對象以前不會存在中間態,即建立了對象實例,但缺乏部分參數,這可能致使對象沒法正確 work。</section></li></ol>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">意圖:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">咱們再理解一次意圖,所謂構建與表示分離,就是指一個對象 Persion
並非簡單的 new Persion()
就能夠實例化出來的,若是能夠,那就是構建與表示一體。所謂構建與表示分離,就是指 Persion
只能描述,而不能經過 new Persion()
實例化,將實例化工做經過 Builder 實現,這樣一樣一個構建過程能夠建立不一樣的 Persion
實例。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">在樂高積木的例子中,經過樂高建立的房子並非 new House()
出來,而是將構建與表示分離了,工廠流水線中咱們建立一個黃桃罐頭,不是經過 new 黃桃罐頭()
,而是經過流水線不一樣拼裝方式來完成,在數據庫例子中,咱們沒有經過 new DB()
的方式建立數據庫,而是經過 Builder 來建立,這都體現了構建與表示的分離。</p>
<h2 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px;"><span class="prefix" style="display: none;"></span><span class="content">結構圖</span><span class="suffix"></span></h2>
<ul data-tool="mdnice編輯器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">Director
指導器,用來指導構建過程。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">Builder
生成器接口,用來提供一系列構建對象的方法,以及最終的 build
生成對象函數,這個函數裏能夠作一些參數校驗。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">ConcreteBuilder
是 Builder
的具體實現。</section></li></ul>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">實際上,Builder 模式抽象層次可高可低,咱們上面三個例子都沒有用到指導器與生成器接口,這是由於在代碼不太複雜的狀況下,可使用簡化模型。</p>
<h2 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px;"><span class="prefix" style="display: none;"></span><span class="content">代碼例子</span><span class="suffix"></span></h2>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">下面例子使用 javascript 編寫。</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> Director {
</pre>
<span/> create(concreteBuilder: ConcreteBuilder) {
<span/> <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 建立了一些零件</span>
<span/> concreteBuilder.buildA();
<span/> concreteBuilder.buildB();
<span/>
<span/> <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 校驗參數已經生成實例</span>
<span/> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> concreteBuilder.build();
<span/> }
<span/>}
<span/>
<span/><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> HouseBuilder {
<span/> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> buildA() {
<span/> <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 建立房屋</span>
<span/> <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// this.xxx = xxx</span>
<span/> }
<span/>
<span/> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> buildB() {
<span/> <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 刷油漆</span>
<span/> }
<span/>
<span/> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> build() {
<span/> <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 最終建立實例</span>
<span/> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> House(<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">/ ..一堆參數 this.xxx.. /</span>);
<span/> }
<span/>}
<span/>
<span/><span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 接下來是正式使用</span>
<span/><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">const</span> director = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> Director();
<span/><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">const</span> builder = HouseBuilder();
<span/><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">const</span> house = director.create(builder);
<span/>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">上面的例子是完整版本的 Builder 模式,抽象了指導器 Director
與生成器 Builder
,只要二者都嚴格按照接口實現,咱們能夠:</p>
<ol data-tool="mdnice編輯器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: decimal;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">替換任意 Director
,使建立的過程作任意修改。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">替換任意 Builder
,使建立的實現作任意修改。</section></li></ol>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">作了任意的改動,均可以獲得不一樣的房子實現,這就是建立與表示分離的好處,咱們能夠經過一樣的構建過程建立不一樣的表示。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">這個 director.create()
:</p>
<ul data-tool="mdnice編輯器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">在搭樂高積木的例子,表示用樂高搭建房屋的過程。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">在工程流水線的例子,表示罐頭的組裝構成。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">在建立數據庫鏈接池的例子,表示數據庫鏈接池的建立過程。</section></li></ul>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">而 Builder
以及其函數 buildA
buildB
等方法表示具體制造方法,好比:</p>
<ul data-tool="mdnice編輯器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">在搭樂高積木的例子,表示如何蓋房子,如何刷油漆。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">在工程流水線的例子,表示如何作一個罐頭,如何添加黃桃。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">在建立數據庫鏈接池的例子,表示如何設置數據庫地址,如何設置用戶名密碼等。</section></li></ul>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">對於數據庫的例子中,咱們不只能夠保證建立對象的便捷性,由於不須要傳入過多參數,也保證了對象的正確校驗,同時生成的實例也是不可變的。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">更重要的是,若是使用完整模式,咱們能夠替換 Director
來修改建立數據庫的方式,替換 Builder
來修改具體方法,好比 .setUserName
這個函數不作具體實現,而是統計性能,build()
函數建立的不是一個數據庫鏈接實例,而是一個測試實例。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">再好比前端同一個方法在 JS 和 Node 環境下運行效果不同,咱們能夠實現 BrowserBuild
與 NodeBuild
,實現相同的接口,這樣能夠共享相同的建立過程,建立不一樣環境能夠運行的實例。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">能夠看到,使用 Builder 模式能夠保證建立對象的便捷與穩定性,還留了足夠的拓展空間改變對象的建立過程與建立方法,具備極強的拓展性。</p>
<h2 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px;"><span class="prefix" style="display: none;"></span><span class="content">弊端</span><span class="suffix"></span></h2>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">任何設計模式都有其適用場景,反過來也說明了在某些場景下不適用。</p>
<ul data-tool="mdnice編輯器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">實例化對象很是繁瑣,重複定義了許多對象成員變量的 set
方法,並且也不如 new
看的直觀,也就是場景足夠簡單時,不須要任何地方都用 Builder 實例化對象。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;">一個對象只有一種表示時,不必作如此地步的抽象。</section></li></ul>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">上面的例子都是相對複雜的,假設咱們的搭房子的例子中,咱們不是用樂高積木搭建,而是用兩塊半成品模板拼起來就獲得一個房子,那就沒有必要使用 Builder 模式,直接 new House()
便可。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">再者,若是咱們只須要生產各類罐頭,而不須要生產汽車,那麼就不必過分抽象 Builder,把建立汽車的方法也囊括進去,最後,若是咱們的對象只有一種表示時,沒有必要抽象 Builder,也就是流水線若是隻生產黃桃罐頭,就不必把各個生產環節變成可拆卸的,由於也沒有從新組合的須要。</p>
<h2 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px;"><span class="prefix" style="display: none;"></span><span class="content">總結</span><span class="suffix"></span></h2>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">Builder 模式對於建立一個複雜對象特別有用,能夠看下圖加深理解:</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">最後總結一下什麼時候適合用 Builder 模式:只有當建立過程容許被構造對象有不一樣表示,或者對象複雜到對象描述與建立對象過程值得分離時,才使用 Builder 設計模式。</p>
<blockquote class="multiquote-1" data-tool="mdnice編輯器" style="border: none; display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; border-left: 3px solid rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.05); color: #6a737d; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px;">
<p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0px; color: black; line-height: 26px;">討論地址是:精讀《設計模式 - Builder 生成器》· Issue #273 · dt-fe/weekly</p>
</blockquote>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">若是你想參與討論,請 點擊這裏,每週都有新的主題,週末或週一發佈。前端精讀 - 幫你篩選靠譜的內容。</p>
<blockquote class="multiquote-1" data-tool="mdnice編輯器" style="border: none; display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; border-left: 3px solid rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.05); color: #6a737d; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px;">
<p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0px; color: black; line-height: 26px;">關注 前端精讀微信公衆號</p>
</blockquote>
<blockquote class="multiquote-1" data-tool="mdnice編輯器" style="border: none; display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; border-left: 3px solid rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.05); color: #6a737d; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px;">
<p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0px; color: black; line-height: 26px;">版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享 3.0 許可證)</p>
</blockquote>javascript
Builder(生成器)屬於建立型模式,針對的是單個複雜對象的建立。前端
意圖:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。java
若是看不懂上面的意圖介紹,沒有關係,設計模式須要在平常工做裏用起來,結合例子能夠加深你的理解,下面我準備了三個例子,讓你體會什麼場景下會用到這種設計模式。git
樂高積木是很典型的隨機拼裝場景,你有不少樂高積木,要搭一個小房子都太複雜了,可能不得不看着說明書一步步操做,這就像建立一個複雜的對象,要傳入很是多的參數,並且順序還不能錯。github
若是不考慮拼裝樂高過程當中的樂趣,你只是想快速獲得一個標準的房子,怎麼樣才能夠最快最省事?web
製做一個罐頭要經歷許多步驟,而其中一些步驟好比製做罐頭是通用的,能夠用這個罐頭裝不少東西,好比紅棗罐頭、黃桃罐頭,那工廠流水線是怎麼作到靈活可拓展的呢?數據庫
創建一個數據庫鏈接池,咱們須要傳入數據庫的地址、用戶名與密碼、還有要建立多少大小的鏈接池,緩存的位置等等。設計模式
考慮到數據庫必須正確鏈接後纔有效,建立時必須校驗傳入的數據庫地址與密碼的正確性,甚至存儲方式與數據庫類型還有關係,這是一個簡單的 new
實例化能夠解決的嗎?緩存
在樂高積木的例子中,咱們爲了獲得一個房子其實不須要關心每個積木應該如何擺放,咱們只要交給組裝工廠(一我的或者一個程序)產出標準房子就好了,這其中參數多是 .setHouseType().build()
設置房屋類型,而不須要 new House(block1, block2, ... block999)
傳遞這些不必的參數。其中組裝工廠就是生成器。微信
在工廠流水線的例子中,流水線就是生成器,一個流水線能夠不經過不一樣組合生成不一樣做用的工廠,黃桃罐頭的流水線能夠理解爲 new Builder().組裝罐頭().放入黃桃().build()
,紅棗罐頭的流水線能夠理解爲 new Builder().組裝罐頭().放入紅棗().build()
,咱們能夠複用生成器最基礎的函數 組裝罐頭()
將其用於建立不一樣的產品中,複用了組裝基礎能力。
在建立數據庫例子中,咱們能夠先設置一些必要的參數再建立,好比 new Builder().setUrl().setPassword().setType().build()
,這樣在最終執行 build
函數的時候,能夠對參數中存在關聯的進行校驗,而獲得的對象也沒法再被修改,這樣比直接暴露數據庫鏈接池對象,再一個值一個值 Set 多了以下好處:
意圖:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。
咱們再理解一次意圖,所謂構建與表示分離,就是指一個對象 Persion
並非簡單的 new Persion()
就能夠實例化出來的,若是能夠,那就是構建與表示一體。所謂構建與表示分離,就是指 Persion
只能描述,而不能經過 new Persion()
實例化,將實例化工做經過 Builder 實現,這樣一樣一個構建過程能夠建立不一樣的 Persion
實例。
在樂高積木的例子中,經過樂高建立的房子並非 new House()
出來,而是將構建與表示分離了,工廠流水線中咱們建立一個黃桃罐頭,不是經過 new 黃桃罐頭()
,而是經過流水線不一樣拼裝方式來完成,在數據庫例子中,咱們沒有經過 new DB()
的方式建立數據庫,而是經過 Builder 來建立,這都體現了構建與表示的分離。
Director
指導器,用來指導構建過程。Builder
生成器接口,用來提供一系列構建對象的方法,以及最終的 build
生成對象函數,這個函數裏能夠作一些參數校驗。ConcreteBuilder
是 Builder
的具體實現。實際上,Builder 模式抽象層次可高可低,咱們上面三個例子都沒有用到指導器與生成器接口,這是由於在代碼不太複雜的狀況下,可使用簡化模型。
下面例子使用 javascript 編寫。
class Director { create(concreteBuilder: ConcreteBuilder) { // 建立了一些零件 concreteBuilder.buildA(); concreteBuilder.buildB(); // 校驗參數已經生成實例 return concreteBuilder.build(); } } class HouseBuilder { public buildA() { // 建立房屋 // this.xxx = xxx } public buildB() { // 刷油漆 } public build() { // 最終建立實例 return new House(/* ..一堆參數 this.xxx.. */); } } // 接下來是正式使用 const director = new Director(); const builder = HouseBuilder(); const house = director.create(builder);
上面的例子是完整版本的 Builder 模式,抽象了指導器 Director
與生成器 Builder
,只要二者都嚴格按照接口實現,咱們能夠:
Director
,使建立的過程作任意修改。Builder
,使建立的實現作任意修改。作了任意的改動,均可以獲得不一樣的房子實現,這就是建立與表示分離的好處,咱們能夠經過一樣的構建過程建立不一樣的表示。
這個 director.create()
:
而 Builder
以及其函數 buildA
buildB
等方法表示具體制造方法,好比:
對於數據庫的例子中,咱們不只能夠保證建立對象的便捷性,由於不須要傳入過多參數,也保證了對象的正確校驗,同時生成的實例也是不可變的。
更重要的是,若是使用完整模式,咱們能夠替換 Director
來修改建立數據庫的方式,替換 Builder
來修改具體方法,好比 .setUserName
這個函數不作具體實現,而是統計性能,build()
函數建立的不是一個數據庫鏈接實例,而是一個測試實例。
再好比前端同一個方法在 JS 和 Node 環境下運行效果不同,咱們能夠實現 BrowserBuild
與 NodeBuild
,實現相同的接口,這樣能夠共享相同的建立過程,建立不一樣環境能夠運行的實例。
能夠看到,使用 Builder 模式能夠保證建立對象的便捷與穩定性,還留了足夠的拓展空間改變對象的建立過程與建立方法,具備極強的拓展性。
任何設計模式都有其適用場景,反過來也說明了在某些場景下不適用。
set
方法,並且也不如 new
看的直觀,也就是場景足夠簡單時,不須要任何地方都用 Builder 實例化對象。上面的例子都是相對複雜的,假設咱們的搭房子的例子中,咱們不是用樂高積木搭建,而是用兩塊半成品模板拼起來就獲得一個房子,那就沒有必要使用 Builder 模式,直接 new House()
便可。
再者,若是咱們只須要生產各類罐頭,而不須要生產汽車,那麼就不必過分抽象 Builder,把建立汽車的方法也囊括進去,最後,若是咱們的對象只有一種表示時,沒有必要抽象 Builder,也就是流水線若是隻生產黃桃罐頭,就不必把各個生產環節變成可拆卸的,由於也沒有從新組合的須要。
Builder 模式對於建立一個複雜對象特別有用,能夠看下圖加深理解:
最後總結一下什麼時候適合用 Builder 模式:只有當建立過程容許被構造對象有不一樣表示,或者對象複雜到對象描述與建立對象過程值得分離時,才使用 Builder 設計模式。
討論地址是: 精讀《設計模式 - Builder 生成器》· Issue #273 · dt-fe/weekly
若是你想參與討論,請 點擊這裏,每週都有新的主題,週末或週一發佈。前端精讀 - 幫你篩選靠譜的內容。
關注 前端精讀微信公衆號
版權聲明:自由轉載-非商用-非衍生-保持署名( 創意共享 3.0 許可證)