<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 许可证)