更多文章参见: 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, 谢谢 :)