前言
书接上回,我们学习了装饰器模式,不知各位道友是否还有印象,本集,我们来学一学工厂相关的设计模式,分为三部, 简单工厂 , 工厂方法 和 抽象工厂 。
先个大家打个预防针,工厂相关的设计模式,都是需要面向对象来实现的,而面向对象只是一种思想上的转变,平时我们实现某个功能,是想着如何操作 Dom ,现在我们需要考虑,把我们要实现的东西转化成一个活生生的对象。
比如在页面左侧增加一个固定窗口展示广告,平常我们会写一个 div ,然后给这个 div 一个图片,来展示广告。右上角再给一个小按钮,点击可以关闭广告。如果采用面向对象,那么我们可以创建一个构造函数,如下:
var Advertisement = function (pic) {
this.pic = pic;
this.close = function () {}
}
一、简单工厂模式
该模式可以管理少部分的同一类,如下方所示,不用去记忆篮球,足球,网球的构造函数,只需要记住 SportsFactory 就可以了。
//篮球基类
var Basketball = function() {
this.intro = '篮球盛行于美国';
}
Basketball.prototype = {
getMember: function() {
console.log('每个队伍需要5人');
},
getBallSize: function() {
console.log('篮球很大');
}
}
//足球基类
var Football = function() {
this.intro = '足球盛行于欧洲';
}
Football.prototype = {
getMember: function() {
console.log('每个队伍需要11人');
},
getBallSize: function() {
console.log('足球很大');
}
}
//网球基类
var Tennis = function() {
this.intro = '网球盛行于澳洲';
}
Tennis.prototype = {
getMember: function() {
console.log('每个队伍需要1人');
},
getBallSize: function() {
console.log('网球很小');
}
}
//运动工厂
var SportsFactory = function(name) {
switch(name) {
case 'NBA':
return new Basketball();
case 'WorldCup':
return new Football();
case 'FrencOpen':
return new Tennis();
}
}
//为世界杯创建一个足球
var football = SportsFactory('WorldCup');
另外对于具有相似属性的不同类,我们也可以利用简单工厂模式,把公共的部分提取出来,然后分别创建差异的部分。
function createPop(type, text) {
//创建一个对象,并对对象拓展属性和方法
var o = new Object();
o.content = text;
o.show = function() {
//显示方法
}
if(type === 'alert') {
//警示框差异部分
}
if(type === 'prompt') {
//提示框差异部分
}
if(type === 'confirm') {
//确认框差异部分
}
return o;
}
//创建警示框
var userNameAlert = createPop('alert', '警告,参数不正确');
上面两种方法由于创建的方式不同,所以在应用时也有存在部分区别,第一种通过实例化对象创建,如果这些类继承同一个父类,那么他们父类上的方法是可以公用的。第二种是创建一个新的对象,然后增强该对象的属性。由于每一个都是新个体,所以他们的方法就不能共用。所以使用哪种方法,要根据需求来灵活选择。
二、安全工厂模式
简单工厂模式里面每次添加都需要修改两个地方,这给需求变更带来了隐患,万一忘记修改另一个地方,还得去重新去定位问题,这时候就需要我们用到工厂模式,把这两个整合到一个里面,方便我们需求变更时,进行修改。
// 安全模式创建的工厂类
var Factory = function (type, content) {
//这一部分是为了保护创建实例时,忘记关键字new的情况
if (this instanceof Factory) {
var s = new this[type](content);
return s;
} else {
return new Factory(type, content);
}
}
//工厂原型中设置创建所有类型数据的基类
Factory.prototype = {
Java: function(content) {},
JavaScript: function(content) {},
PHP: function(content) {}
}
三、抽象工厂模式
通过创建抽象类,来实现工厂模式,这种方法称为抽象工厂模式。
大家注意,抽象类是一种可以创建,但不能使用的类,一旦使用就会报错。那么问题来了,既然不能用,那我们造它干嘛。
抽象类的使用方法主要是用于大型应用中,当使用继承时,总有一些子类会继承父类,父类需要预先定义一些必要的方法,但没有具体实现,比如下面的 getPrice 方法。通过子类继承的 getPrice 方法如果没有重写,那么一旦使用,就会友好提示。
//汽车抽象类,当使用其实例对象的方法时会抛出错误
var Car = function() {};
Car.prototype = {
getPrice: fucntion() {
return new Error('抽象方法不能调用');
},
getSpeed: fucntion() {
return new Error('抽象方法不能调用');
}
}
这个 Car 其实什么都不能做,创建时没有任何属性,然而原型上面的方法也不能使用,否则会报错。但是在继承上很有用,上面定义了一种类,并且定义了该类的方法,如果在子类中没有重写这个方法,那么该方法调用时就会报错。这一点很有用,在一些大型应用上面,父类会经常定义一些必要的方法,一旦子类创建了一个对象,该对象就会具备这些必要方法,如果没有重写,则会友好提示。
抽象类作用,定义一个产品族,并声明一些必备方法,如果子类中没有去重写,就会抛出错误。而抽象工厂方法不会创建对象,而是要求一个对象具备完整的功能。
//抽象工厂方法
var VehicleFactory = function(subType, superType) {
//判断抽象工厂中是否有该抽象类
if(typeof VehicleFactory[superType] === 'function') {
//缓存类
function F() {};
//继承父类属性和方法
F.prototype = new VehicleFactory[superType] ();
//将子类的constructor指向子类
subType.constructor = subType;
//子类原型继承父类
subType.protype = new F();
} else {
//不存在该抽象类,抛出错误
throw new Error('未创建该抽象类');
}
}
//小汽车抽象类
VehicleFactory.Car = function() {
this.type = 'car';
}
VehicleFactory.Car.prototype = {
getPrice: fucntion() {
return new Error('抽象方法不能调用');
},
getSpeed: fucntion() {
return new Error('抽象方法不能调用');
}
}
//公交车抽象类
VehicleFactory.Bus = function() {
this.type = 'bus';
}
VehicleFactory.Bus.prototype = {
getPrice: fucntion() {
return new Error('抽象方法不能调用');
},
getSpeed: fucntion() {
return new Error('抽象方法不能调用');
}
}
//货车抽象类
VehicleFactory.Truck = function() {
this.type = 'truck';
}
VehicleFactory.Truck.prototype = {
getPrice: fucntion() {
return new Error('抽象方法不能调用');
},
getSpeed: fucntion() {
return new Error('抽象方法不能调用');
}
}
抽象工厂其实是一个子类继承父类的方法,通过寄生式继承,继承父类时需要注意一点,在对过度类的原型继承时,不是继承父类的原型,而是通过 new 关键字 复制一个父类的实例,这样做是为了不仅要进程父类原型上的方法,还要继承父类的对象属性。
抽象工厂因为不需要实例化对象,所以只需要一份,因此直接给抽象工厂添加类的属性即可。
如何使用
我们只需要一些子类产品,然后让子类继承相应的产品组抽象类就行了
//宝马汽车子类
var BMW = function(price, speed) {
this.price = price;
this.speed = speed;
}
//使用抽象工厂对Car类进行继承
VehicleFactory(BMW, 'Car');
BMW.prototype.getPrice = function() {
return this.price;
}
BMW.prototype.getSpeed = function() {
return this.speed;
}
通过抽象方法,可以知道每个子类是哪一个类别,然后他们也具备了必须的属性和方法。
var car = new BMW(1000000, 100);
console.log(car.getPrice()); //1000000
console.log(car.type()); //car
抽象工厂模式是设计模式中最抽象的一种,也是创建模式中,唯一一种抽象化创建模式,他创建的是一个类簇,可以制定类的结构,但是由于 JS 不支持抽象化创建与虚拟方法,导致这种模式不能像在其他面向对象语言中应用的那么广泛。毕竟 abstract 在 JS 中还是一个关键字。
本节介绍了工厂相关的方法,分别是管理简单类的简单工厂方法,把类写到原型上的安全工厂方法,还有创建子类的抽象工厂方法。下一节我们一起学习对整体对象局部创建,来达到功能复用最大化。