更多文章参见: https://github.com/elevenbeans/elevenbeans.github.io
前言 本系列可以看作是我个人对于 Addy Osmani 的著作《Learning JavaScript Design Patterns》中内容的提炼,类似阅读笔记,目的是为了简单快速、又不失全面地了解设计模式的相关概念、特点、分类及其在 Javascript 中的实际用法。
分为五篇:
概述
创建型设计模式(本篇)
结构型
行为型
MV*(待补充)
创建型设计模式 1. Constructor(构造器) 模式 基本的 Constructor Javascript 不支持类,但支持 constractor 构造器函数。可以通过构造器前加new关键字告诉 Javascript 实例化一个对象。一个构造器的例子:
1 2 3 4 5 6 7 8 9 10 11 12 function Car (model, year, miles ) { this .model = model; this .year = year; this .miles = miles; this .toString = function ( ) { return this .model + 'has done' + this .miles + 'miles' ; }; } var benz = new Car ('GLS' , 2018 , 20000 );console .log (benz.toString ());
存在的问题是: this.toString() 这样的函数在每次创建新的对象的时候都会重新定义,这样很不理想,这类共有方法应该在所有实例间共享。
带原型的 Constructor 1 2 3 4 5 6 7 8 9 10 11 12 function Car (model, year, miles ) { this .model = model; this .year = year; this .miles = miles; } Car .prototype .toString = function ( ) { return this .model + 'has done' + this .miles + 'miles' ; }; var benz = new Car ('GLS' , 2018 , 20000 );var byd = new Car ('qin' , 2016 , 68000 );
如此一来,toString() 方法的单一实例就能通过原型链在 Car 的实例对象 benz 和 byd 中共享。
2. Factory(工厂) 模式 工厂模式并不显式要求使用构造函数。他通过提供一个通用的接口来创建对象 ,在接口的入参中指定需要创建的对象类型。具体例子如下:
首先定义不同类型的构造函数供给工厂消费。
1 2 3 4 5 6 7 8 9 10 11 12 function Car (opt ) { this .doors = opt.doors || 4 ; this .state = opt.state || 'new' ; this .color = opt.color || 'slivler' ; } function Truck (opt ) { this .state = opt.state || 'used' ; this .whellSize = opt. whellSize || 'large' ; this .color = opt.color || 'blue' ; }
然后定义 vehicle 工厂:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function VehicleFactory ( ) { }VehicleFactory .prototype .vehicleClass = Car ;VehicleFactory .prototype .createVehicle = function (opt ) { if (opt.vehicleType === 'car' ) { this .prototype .vehicleClass = Car ; } else if (opt.vehicleType === 'truck' ) { this .prototype .vehicleClass = Truck ; } else { this .prototype .vehicleClass = Car ; } return new this .prototype .vehicleClass (opt); };
工厂创建完毕,具体实例的创建方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var factory = new VehicleFactory (); var carIntance = factory.createVehicle ({ vehicleType : 'car' , color : 'yellow' , door : 6 }); var trunkIntance = factory.createVehicle ({ vehicleType : 'truck' , color : 'yellow' , state : 'new' , wheelSize : 'small' });
一般来讲,Factory 模式适用于
复杂度较高的对象和组件 / 处理很多共享属性的对象或组件
需要在不同环境生成不同类型的实例对象的时候
3. Abstract(抽象) 模式 对于上述的工厂,createVehicle 一经定义,就只支持 Car 和 Truck 两种类型的实例产出,无法动态增加新的实例类型。
所以需要抽离出来实例对象的产出细节,封装一个通用工厂。
抽象模式(在工厂模式中的)实践如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 var AbstractVehicleFactory = (function ( ){ var types = []; return { getVehicle : function (type, opt ) { var Vehicle = types[type]; return (Vehicle ) ? return new Vehicle (opt) : null ; }, registerVehicle : function (type, Vehicle ) { var proto = Vehicle .prototype ; if (proto.drive && proto.breakDown ) { types[type] = Vehicle ; } return AbstractVehicleFactory ; } } })(); AbstractVehicleFactory .registerVehicle ('car' , Car );var car = AbstractVehicleFactory .getVehicle ('car' , { color : 'yellow' , door : 6 });
4. Prototype(原型) 模式 原型模式是一种基于现有对象模版,通过克隆方式创建新对象的一种模式。
该模式是 Javascript 语言本身的优势。具体例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var car = { name : 'Ford Escort' , drive : function ( ) { console .log ('Weeee. I am driving' ); }, panic : function ( ) { console .log ('Wait. How do you stop this?' ); } } var myCar = Object .create (car);console .log (myCar)
当然,Object.create() 完全可以通过第二个参数实现差异化继承。
1 2 3 4 5 6 7 8 9 10 Object .create (orgObj, { id : { value : 23324324 , enumerable : true }, 'model' : { value : 'Ford' , enumerable : true } })
如果不用 Object.create() 实现 Prototype(原型) 模式也很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 car.init = function (opt ) { this .name = opt; }; function Car ( ) { function C ( ) {}; C.prototype = car; var c = new C (); return c; } var myCar = Car ();myCar.init ('Benz' );
本质其实就是将源对象赋值给目标对象的原型,初始化可以通过为源对象预留 API(本例中为init())来实现。
5. Singleton(单例) 模式 一句话来形容,Singleton(单例) 模式限制类的实例化次数只能是一次。也就是说,在不存在该类实例的情况下,可以通过方法来创建实例;如果实例已经存在,返回该实例对象的引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 function Car ( ){ this .name = 'Singleton Car' ; }; var Singleton = { instance : null , getInstance : function ( ) { if (!this .instance ) { this .instance = new Car (); } return this .instance ; } };
就像这样当我们每次调用 Singleton.getInstance() 时,就会得到唯一的实例。
如此,可以将我们反复使用的对象只创建一次。减少不必要开销。
Singleton 模式还可以结合 JS 闭包反复引用内存中的同一对象,使运用程序更快速的执行。
6. Builder(生成器) 模式 将一个复杂对象的构造与它的表示相分离,使同样的创建过程可有不同的表示,这就叫做建造者模式。主要角色:
Builder 这个接口类,定义这个建造者,统一的可操作的行为方式,它表示一个复杂的结构对象;
Director 这个指挥者,用于指导 Builder 实例的执行过程跟形式,用于与 Builder 的实例 表现 相分离,用于指导 这个 Builder 实例 按某规则顺序来创建生成 产品结果;
ResultObject 创建的结果都会生成一个结果对象;这是具体创建者根据 Director 指导创建的结果;
建造者模式实际,就是一个指挥者 ,一个建造者 ,外加一个使用指挥者调用具体建造者工作、并得从具体建造者得出结果的客户 。模拟场景:
一户家人要建房子(ResultObject / 结果),但房子主人或家里其他人是不懂得如何去建房子的,所以他得去请几个工人(Builder / 建造者),这个建房子的队伍还得有个工头(Director / 指挥者),来按房主人的想法来建一套房子,工头按房主人(客户 )的要求设计要求工人如何如何做。
工头说,第一步先把房整体骨架搭起来,第二步睡房建造好,第三步把厨房装饰好,第四步把客厅建造装饰完毕…
工头是不做事的,但具体工人必须按照工头的要求来做,第一步,第二步的这样步骤来建造,直至整个房子完成;
工人必须要有创建这个房屋的所有技能,即建骨架,装饰卧室等,即工人所做的事,或所具有的能力,必须大于或等于工头要求要做的事,或具有的能力;
即工头是个组织者,而工人提供技能;
实例代码:
1. Builder 类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function workerBuilder ( ) { this .workOne = function ( ) { } this .workTwo =function ( ) { } this .workThree =function ( ) { } this .workFour =function ( ) { } this .getResult = function ( ) { var house = new House (); return house; } }
workBuilder 是具体建造者类,workOne, Two是要做的事情,建骨架等。 当然 workBuilder 可以多建几个,用于表示建造者对于每个工作执行的方法不一样;但工作内容是一样的;
2. Director 类 1 2 3 4 5 6 7 8 9 10 11 function Director ( ) { this .construct = function (builder ) { builder.workOne (); builder.workTwo (); builder.workThree (); builder.workFour (); } }
指挥者类下的指导方法,有对建造者的回调引用,内容包括建者工作内容几项或全部; 指挥者对建造者要做的事情进行组织跟安排。
3. ResultObject 1 2 3 4 5 6 7 function House ( ) { this .HouseFrame = '' ; this .Room = '' ; this .Kitchen = '' ; this .LivingRoom = '' ; }
4. 使用方法 1 2 3 4 var director = new Director (); var builder = new workBuilder (); director.construct (builder); var house = builder.getResult ();
Referrence
https://www.2cto.com/kf/201412/360781.html
创建型设计模式汇总介绍到此结束,持续关注请 Star and Watch This github repo , 谢谢 :)