最近阅读了几本设计模式方面的书籍,学习之余整理下来,方便之后的概括和梳理javascript
创造工厂模式是一种建立性模式
,也就是一种建立对象的最佳实践.首先咱们须要理解:html
想象一个场景:若是你要求去买一些东西:板烧鸡腿汉堡
,可乐
和薯条
,那么人们会很是天然的跑去麦当劳去购买对吧.java
为何咱们会想到去麦当劳呢?由于这些东西都是一类食物,而后麦当劳做为一个'工厂',能够一条龙的提供给消费者,若是没有麦当劳,那么咱们须要分别去可乐,薯条和板烧鸡腿汉堡的店面去分别买这些食物,那么购买效率会很低.因此能够说麦当劳就是一个销售食物的工厂模式.设计模式
因此咱们能够这样理解工厂模式,把相关的多个类(薯条,可乐等)提供一个统一入口的一个模式,让你从一个入口就能够得到多个类,提升工做效率.安全
可是对于工厂模式也会有三种类型的实现方式,分别是:简单工厂模式,工厂方法模式和抽象工厂模式.它们分别是在各自基础上有必定的改进.函数
也被叫作静态工厂模式(Simple Factory Patter),主要用于建立同一类的对象
.学习
适用状况:若是被要求写一些球类的实现,那么通常状况的话咱们会这样实现:this
var Football = function(){ this.name = "football"; .... } var Basketball = function(){ this.name = "basketball"; .... }
这种写法有一些缺陷:返回了多个类,不便于他人使用.
所以咱们考虑把这些相似的类封装到一个工厂里面,也就有了咱们的简单工厂模式:prototype
var BallFactory; !(function(){ BallFactory = function(type,cfg){ var Football = function(cfg){ this.name = 'football'; console.log(this.name + " in the prototype"); }; Football.prototype = { call:function(){console.log(this.name)}, sell:function(){} }; var Basketball = function(cfg) { this.name = "basketball"; console.log(this.name); }; var cfg = cfg ||{}; switch(type){ case 'football': return new Football(cfg); break; case 'basketball': return new Basketball(cfg); break; } }; })(); var aFootball = BallFactory('football'); aFootball.call();
所以使用BallFactory把这些内容包裹起来给其余人使用就会避免返回了多个类的问题,因此这就是简单工厂模式想解决的问题:统一建立的入口
.设计
固然在实际的使用过程当中,咱们会使用一些其它技巧:
若是工厂的产品中有不少重复部分,那么咱们须要把重复的部分抽象出来成为共同的部分,不一样的部分放入switch里面:
var GetChildren = function(cfg){ cfg = cfg||{}; this.name = cfg.name; this.height = cfg.height; this.speak = function(){}; //抽离出不一样的部分 switch(cfg.gender){ case "boy": this.gender = cfg.gender; this.moustouch = .... .....; //特有部分 break; case "girl": this.gender = cfg.gender; ....... break; } return this; } var aBoy = GetChildren({.....});
在简单工厂模式的基础上,咱们已经解决了入口不统一的问题,可是还有一个问题没有解决:
加入一个新的类须要修改多部分:首先咱们须要在BallFactory工厂内部加入如何实现,而后加到switch部分;因此这是一次修改的需求,咱们须要修改多个地方.
那么咱们看能不能尝试更抽象一点,尽量减小须要修改的地方;
var BallFactory = function(type,cfg){ this.name = cfg.name; //共同的部分放在这里 return this[type](cfg); }; BallFactory.prototype = { football:function(cfg){ console.log("这里加入和football相关的独特内容" +this.name); }, basketball:function(cfg) { console.log("这里加入和basketball相关的独特内容" +this.name); } }; var aBall = new BallFactory("football",{name:"football"}); //football in the prototype
因此这里加入了一个return this[type](cfg)
的方法自动代替了以前的switch的方法.之后须要加入内容只须要修改BallFactory的prototype就能够了.
固然咱们还能够添加一种安全模式
来解决若是不在构造函数前面加上new的话,会报错的问题.解决的思路实际上是把new封装在构造函数以内:
var BallFactory = function(type,cfg){ if(!(this instanceof BallFactory)){ return new BallFactory(type,cfg); //多一行判断即:若是没有带new,我本身帮你new一个返回就好; } this.name = cfg.name; return this[type](cfg); }; var aBall = BallFactory("football",{name:"football"}); //这里若是掉了new也会正常执行;
因此,咱们能够看到的是,咱们使用简单工厂模式解决了入口不统一的问题,而后使用工厂模式解决了修改地点不统一的问题
通常来讲,抽象工厂在大型项目的使用更多,大概的思路是在父类里面设计好接口(没有具体实现),具体的实现等到了子类再重写.
这里借用一个张容铭的<JavaScript设计模式>的一个例子:
var Car = function(){}; Car.prototype = { getPrice:function(){ throw new Error("抽象方法不能调用")}, getSpeed:function(){ throw new Error("抽象方法不能调用")} }; //这里使用Object.create()继承,子类到父类中会多一个中间过渡函数Temp(){};防止在子类的prototype覆盖父类;可见参考资料 aBMW = Object.create(Car.prototype); aBMW.getPrice(); // 抽象方法不能调用 aBMW.getPrice = function(){ console.log("I am getting price"); }; aBMW.getPrice(); //I am getting price
父类定义好接口,具体实现延迟到子类才实现.
因此总结来讲:
简单工厂模式解决了入口不统一的问题,
工厂模式解决了修改地点不统一的问题,
抽象工厂模式解决了子类实现不规范的问题