zoukankan      html  css  js  c++  java
  • javascript设计模式-生成器模式(Builder)

      1 <!doctype html>
      2 <html lang="en">
      3 <head>
      4     <meta charset="UTF-8">
      5     <title>生成器模式</title>
      6 </head>
      7 <body>
      8     
      9     <script>
     10 /**
     11  * 生成器模式
     12  *
     13  * 定义:
     14  * 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
     15  *
     16  * 本质:
     17  * 分离整体构建算法和部件构造。
     18  * 生成器模式的重心在于分离整体构建算法和部件构造,而分步骤构建对象不过是整体构建算法的一个简单实现,或者说是一个附带产物。
     19  *
     20  * 要实现同样的构建过程可以创建不同的表现,那么一个自然的思路就是先把构建过程独立出来,在生成器模式中把它称为指导者,由它来指导装配过程,但是不负责每步具体的实现。当然,光有指导者是不够的,必须要有能具体实现每步的对象,在生成器模式中称这些实现对象为生成器。
     21  * 这样一来,指导者就是可以重用的构建过程,而生成器是可以被切换的具体实现。
     22  *
     23  * 功能:
     24  * 主要功能是构建复杂的产品,而且是细化的,分步骤的构建产品,也就是生成器模式中在一步一步解决构造复杂对象的问题。更为重要的是,这个构建的过程是统一的,固定不变的,变化的部分放到生成器部分了,只要配置不同的生成器,那么同样的构建过程,就能构建出不同的产品来。
     25  * 生成器模式的重心在于分离构建算法和具体的构造实现,从而使得构建算法可以重用。具体的构造实现可以很方便地扩展和切换,从而可以灵活的组合来构造出不同的产品对象。
     26  *
     27  * 构成:
     28  * 1.一个部分是Builder接口,这里是定义了如何构建各个部件,也就是知道每个部件功能如何实现,以及如何装配这些部件到产品中去。
     29  * 2.另外一个部分是Director,Director是知道如何组合来构建产品,也就是说Director负责整体的构建算法,而且通常是分步骤地来执行。
     30  * 不管如何变化,Builder模式都存在这么两个部分,一个部分是部件构造和产品装配,另一个部分是整体构建的算法。
     31  *
     32  * 使用:
     33  * 应用生成器模式的时候,可以让客户端创造Director,在Director里面封装整体在、构建算法,然后让Director去调用Builder,让Builder来封装具体部件的构建功能。
     34  * 还有一种退化的情况,就是让客户端和Director融合起来,让客户端直接去操作Builder,就好像是指导者自己想要给自己构建产品一样。
     35  *
     36  * 何时使用:
     37  * 1.如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时。
     38  * 2.如果同时一个构建过程有着不同的表示时。
     39  * 
     40  */
     41 
     42 // 示例代码
     43 
     44 // 生成器对象,创建一个产品对象所需的各个部件的操作
     45 var Builder = function(){
     46     // 生成器最终构建的产品对象
     47     var resultProduct;
     48 
     49 // 获取生成器最终构建的产品对象
     50     this.getResult = function(){
     51         return resultProduct;
     52     };
     53 
     54     this.buildPart = function(){
     55         // 构建某个部件的功能处理
     56     };
     57 }
     58 
     59 // 指导者,指导使用生成器的接口来构建产品的对象
     60 // 传入生成器对象
     61 var Director = function(builder){
     62     this.builder = builder;
     63 };
     64 Director.prototype = {
     65     // 通过使用生成器接口来构建最终的产品对象
     66     construct: function(){
     67         this.builder.buildPart();
     68     }
     69 };
     70 
     71 (function(){
     72 
     73     // 生成器接口,定义创建一个输出文件对象所需的各个部件的操作
     74     var Builder = function(){};
     75     Builder.prototype = {
     76         // 构建输出文件的Header部分
     77         buildHeader: function(ehm){},
     78         // 构建输出文件的Body部分
     79         buildBody: function(mapData){},
     80         // 构建输出文件的Footer部分
     81         buildFooter: function(efm){}
     82     };
     83 
     84     // 实现到处数据到文本文件的生成器对象    
     85     var TxtBuilder = function(){
     86         this.stringBuffer = '';
     87     };
     88   TxtBuilder.prototype = {
     89     __proto__: Builder.prototype,
     90 
     91     buildBody: function(mapData){
     92       var tblName, edm;
     93       for(tblName in mapData) {
     94         this.stringBuffer += tblName + "
    ";
     95 
     96         for(edm in mapData[tblName]) {
     97           this.stringBuffer += edm.getProductId() + ',' +
     98             edm.getPrice() + ',' + edm.getAmount() + '
    ';
     99         }
    100       }
    101     },
    102     buildFooter: function(efm){
    103       this.stringBuffer += efm.getExportUser();
    104     },
    105     buildHeader: function(ehm){
    106       this.stringBuffer += ehm.getDepId() + ',' +
    107         ehm.getExportDate() | '
    ';
    108     },
    109     getResult: function(){
    110       return new Buffer(this.stringBuffer);
    111     }
    112   };
    113 
    114     // 实现导出数据到XML文件的生成器对象
    115     var XmlBuilder = function(){
    116         this.stringBuffer = '';
    117     };
    118   XmlBuilder.prototype = {
    119     __proto__: Builder,
    120     buildBody: function(){},
    121     buildFooter: function(){},
    122     buildHeader: function(){},
    123     getResult: function(){}
    124   };
    125 
    126     // 指导者,指导使用生成器的接口来构建输出的文件的对象
    127     var Director = function(builder){
    128         // 传入生成器对象
    129         this.builder = builder;
    130     };
    131     Director.prototype = {
    132         // 指导生成器构建最终的输出的文件的对象
    133         construct: function(ehm, mapData, efm){
    134             this.builder.buildHeader(ehm);
    135             this.builder.buildBody(mapData);
    136             this.builder.buildFooter(efm);
    137         }
    138     };
    139 
    140     var textBuilder = new TxtBuilder();
    141     var director = new Director(textBuilder);
    142     director.construct(ehm, mapData, efm);
    143 
    144     var xmlBuilder = new XmlBuilder();
    145     var director2 = new Director(director);
    146     director2.construct(ehm, mapData, efm);
    147 
    148 }());
    149 
    150 /*
    151 生成器模式的实现
    152 
    153 1。生成器的实现
    154 实际上在Builder接口的实现中,每个部件构建的方法里面,除了部件装配外,也可以实现如何具体地创建各个部件对象。也就是说每个方法都可以由两部分功能,一部分是创建部件对象,另一部分是组装部件。
    155 在构建部件的方法里面可以实现选择并创建具体的部件对象,然后再把这个部件对象组装到产品对象中去。这样一来,Builder就可以和工厂方法配合使用了。
    156 再进一步,如果在实现Builder的时候,只有创建对象的功能,而没有组装的功能。那么这个时候的Builder实现跟抽象工厂的实现是类似的。
    157 这种情况下,Builder接口就类似抽象工厂的接口,Builder的具体实现就类似于具体的工厂,而且Builder接口里面定义的创建各个部件的方法也是有关联的,这些方法是构建一个复杂对象所需要的部件对象。
    158 
    159 2.指导者的实现
    160 在生成器模式里面,指导者承担的是整体构建算法部分,是相对不变的部分。因此在实现指导者的时候,把变化的部分分离出去很重要。
    161 其实指导者分离出去的变化部分,就到了生成器那里,指导者知道整体的构建算法,却不知道如何具体的创建和装配部件对象。
    162 因此真正的指导者实现,并不仅仅是简单地按照一定的顺序调用生成器的方法来生成对象。应该是有较为复杂的算法和运算过程,在运算过程中根据需要,才会调用生成器的方法来生成部件对象。
    163 
    164 3.指导者和生成器的交互
    165 在生成器模式里面,指导者和生成器的交互是通过生成器的buildPart方法来完成的。
    166 指导者通常会实现比较复杂的算法或者是运算过程,在实际开发中很可能会有以下的情况:
    167 1) 在运行指导者的时候,会按照整体构建算法的步骤进行运算,可能先运行前几步运算,到了某一步骤,需要具体创建某个部件对象了,然后就调用Builder中创建相应部件的方法来创建具体的部件。同时,把前面运算得到的数据传递给Builder,因为在Builder内部实现创建和组装部件的时候,可能会需要这些数据。
    168 2) Builder创建完具体的部件对象后,会把创建好的部件对象返回给指导者,指导者继续后续的算法运算,可能会用到已经创建好的对象。
    169 3)如此反复下去,知道整个构建算法运行完成,那么最终的产品对象也就创建好了。
    170 
    171 可以看出指导者和生成器是需要交互的,方式就是用过生成器方法的参数和返回值,来回地传递数据。事实上,指导者使用过委托的方式把功能交给生成器去完成。
    172 
    173 4.返回装配好的产品的方法
    174 标准的生成器模式中,在Builder实现里面会提供一个返回装配好的产品的方法,在Builder接口上是没有的。它考虑的是最终的对象一定要通过部件构建和装配,才算真正创建了,而具体干活的就是Builder实现,虽然指导者也参与了,但是指导者是不负责具体的部件创建和组装的,因此客户端是从Builder实现里面获取最终装配好的产品。
    175 
    176 5.关于被构建的产品的接口
    177 在使用生成器模式的时候,大多数情况下是不知道最终构建出来的产品是什么,所以一般不需要对产品定义抽象接口,因为最终构建的产品千差万别,给这些产品定义公共接口几乎是没有意义的。
    178  */
    179 
    180 // 使用生成器模式构建复杂对象
    181 
    182 var InsuranceContract = (function(){
    183     // 保险合同对象
    184 var InsuranceContract = function(builder){
    185     this.contractId = builder.getContractId();
    186     this.personName = builder.getPersonName();
    187     this.companyName = builder.getCompanyName();
    188     this.beginDate = builder.getBeginDate();
    189     this.endDate = builder.getEndDate();
    190     this.otherDate = builder.getOtherDate();
    191 };
    192 InsuranceContract.prototype = {
    193     someOperation: function(){
    194         console.log('Now in Insurance Contract someOperation = ' + this.contractId);
    195     }
    196 };
    197 
    198 // 构造保险合同对象的构造器
    199 var ConcreteBuilder = function(contractId, beginDate, endDate){
    200     this.contractId = contractId;
    201     this.beginDate = beginDate;
    202     this.endDate = endDate;
    203 };
    204 ConcreteBuilder.prototype = {
    205     setPersonName: function(personName){
    206         this.personName = personName;
    207         return this;
    208     },
    209     setCompanyName: function(companyName){
    210         this.companyName = companyName;
    211         return this;
    212     },
    213     setOtherDate: function(otherData){
    214         this.otherData = otherData;
    215         return this;
    216     },
    217     getContractId: function(){
    218         return this.contractId;
    219     },
    220     getPersonName: function(){
    221         return this.personName;
    222     },
    223     getCompanyName: function(){
    224         return this.companyName;
    225     },
    226     getBiginDate: function(){
    227         return this.beginDate;
    228     },
    229     getEndDate: function(){
    230         return this.endDate;
    231     },
    232     getOtherData: function(){
    233         return this.otherData;
    234     },
    235 
    236     // 构建真正的对象并返回
    237     // 添加一些约束规则
    238     build: function(){
    239         if(!this.contractId || this.contractId.trim().length === 0) {
    240             throw new Error('合同编号不能为空');
    241         }
    242         var signPerson = this.personName && this.personName.trim().length > 0;
    243         var signCompany = this.companyName && this.companyName.trim().length > 0;
    244 
    245         if(signPerson && signCompany) {
    246             throw new Error('一份合同不能同时与人和公司签订');
    247         }
    248 
    249         if(!signPerson && !signCompany) {
    250             throw new Error('一份合同不能没有签订对象');
    251         }
    252 
    253         if(this.beginDate <= 0) {
    254             throw new Error('合同必须有保险开始生效的日期');
    255         }
    256 
    257         if(this.endDate <= 0) {
    258             throw new Error('合同必须有保险失效的日期');
    259         }
    260 
    261         if(this.endDate <= this.beginDate) {
    262             throw new Error('保险失效的日期必须大于生效日期');
    263         }
    264 
    265         return new InsuranceContract(this);
    266     }
    267 };
    268 
    269 // 把构建对象和被构建对象合并
    270 InsuranceContract.ConcreteBuilder = ConcreteBuilder;
    271 
    272 return InsuranceContract;
    273 }());
    274 
    275 
    276 var builder = new InsuranceContract.ConcreteBuilder('001', 123456, 6789);
    277 var contract = builder.setPersonName('luke').setOtherData('test').build();
    278 contract.someOperation();
    279 
    280 
    281 /*
    282 相关模式
    283 
    284 1.生成器模式与工厂方法模式
    285 
    286 这两个模式可以组合使用。
    287 生成器模式的Builder实现中,通常需要选择具体的部件实现。一个可行的方案就是实现成为工厂方法,通过工厂方法来获取具体的部件对象,然后再进行部件的装配。
    288 
    289 2.生成器模式与抽象工厂模式
    290 
    291 这两个模式既相似又有区别,也可以组合使用。
    292 区别:抽象工厂模式的主要目的是创建产品簇,这个产品簇里面的单个产品就相当于是构成一个复杂对象的部件对象,抽象工厂对象创建完成后就立即返回整个产品簇;而生成器模式的主要目的是按照构造算法,一步一步来构建一个复杂的产品对象,桐城要等到整个构建过程结束以后没才会得到最终的产品对象。
    293 组合使用:在生成器模式的Builder实现中,需要创建各个部件对象,而这些部件对象是有关联的,通常是构成一个复杂对象的部件对象。也就是说,Builder实现中,需要获取构成一个复杂对象的产品簇,就可以使用抽象工厂模式来实现。有抽象工厂模式负责部件对象创建,Builder实现里面则主要负责产品对象整体的构建了。
    294 
    295 3.生成器模式和模板方法模式
    296 
    297 这也是两个非常相似的模式。
    298 模板方法模式主要是用来定义算法的骨架,把算法中某些步骤延迟到子类中实现。生成器模式Director用来定义整体的构建算法,把算法中某些涉及到具体部件对象的创建和装配功能,委托给具体的Builder来实现。
    299 从实质上看,都是定义一个固定的算法骨架,然后把算法中的某些具体步骤交给其他类来实现,都能实现整体算法步骤和某些具体步骤实现的分离。
    300 区别:
    301 1).目的:生成器模式用来构建复杂对象。模板模式用来定义算法骨架,尤其是一些复杂的业务功能的处理算法的骨架;
    302 2)。实现:生成器模式采用委托的方法,模板模式采用继承的方法。
    303 3)。复杂度:生成器模式需要组合Director和Builder对象,然后才能开始构建,要等构建后才能获得最终的对象,而模板方法就没这么麻烦,直接使用子类对象即可。
    304 
    305 4.生成器模式和组合模式
    306 
    307 可以组合使用。
    308 对于复杂的组合结构,可以使用生成器模式来一步一步构建。
    309  */
    310 
    311 
    312 // http://www.dofactory.com/javascript-builder-pattern.aspx
    313 
    314 function Shop() {
    315     this.construct = function(builder) {
    316         builder.step1();
    317         builder.step2();
    318         return builder.get();
    319     }
    320 }
    321 
    322 function CarBuilder() {
    323     this.car = null;
    324     this.step1 = function() {
    325         this.car = new Car();
    326     };
    327     this.step2 = function() {
    328         this.car.addParts();
    329     };
    330     this.get = function() {
    331         return this.car;
    332     };
    333 }
    334 
    335 function TruckBuilder() {
    336     this.truck = null;
    337     this.step1 = function() {
    338         this.truck = new Truck();
    339     };
    340     this.step2 = function() {
    341         this.truck.addParts();
    342     };
    343     this.get = function() {
    344         return this.truck;
    345     };
    346 }
    347 
    348 function Car() {
    349     this.doors = 0;
    350     this.addParts = function() {
    351         this.doors = 4;
    352     };
    353     this.say = function() {
    354         log.add("I am a " + this.doors + "-door car");
    355     };
    356 }
    357 
    358 function Truck() {
    359     this.doors = 0;
    360     this.addParts = function() {
    361         this.doors = 2;
    362     };
    363     this.say = function() {
    364         log.add("I am a " + this.doors + "-door truck");
    365     };
    366 }
    367 
    368 // log helper
    369 var log = (function () {
    370     var log = "";
    371     return {
    372         add: function (msg) { log += msg + "
    "; },
    373         show: function () { alert(log); log = ""; }
    374     }
    375 })();
    376 
    377 function run() {
    378     var shop = new Shop();
    379 
    380     var carBuilder = new CarBuilder();
    381     var truckBuilder = new TruckBuilder();
    382 
    383     var car = shop.construct(carBuilder);
    384     var truck = shop.construct(truckBuilder);
    385 
    386     car.say();
    387     truck.say();
    388 
    389     log.show();
    390 }
    391     </script>
    392 </body>
    393 </html>
  • 相关阅读:
    nno_setup制作升级包必须面临的几个问题 2
    如何运用inno在安装和卸载时提示用户结束进程?
    inno安装卸载时检测程序是否正在运行卸载完成后自动打开网页-代码无效
    Inno Setup 插件大全
    INNO安装卸载自动结束进程插件使用
    Inno Setup 在安装程序开始前和卸载程序开始前,检查并关闭运行的进程
    INNO:检测程序是否已经安装,是则弹出卸载提示。
    Inno Setup 安装、卸载前检测进程或服务
    断点续传2
    解决 IDEA 创建 Gradle 项目没有src目录问题
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/3471983.html
Copyright © 2011-2022 走看看