工厂模式是指不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,这个函数就被称为工厂。工厂模式又可以分为:简单工厂、工厂方法、抽象工厂。
1、 简单工厂模式
简单工厂模式又叫静态工厂方法,由一个工厂对象决定创建某一种产品对象类的实例。主要用来创建同一类对象。
比如我们玩游戏的时候,通常会有多种角色,每个角色又有自己的技能。
var Warrior = function () {
this.name = '战士'
this.skill = ['刺杀', '十字斩']
}
var Master = function () {
this.name = '法师'
this.skill = ['雷电术', '保护盾']
}
var Warlock = function () {
this.name = '道士'
this.skill = ['火符', '召唤神兽']
}
var RoleFactory = function (role) {
switch (role) {
case '战士':
return new Warrior();
case '法师':
return new Master();
case '道士':
return new Warlock();
default:
throw new Error('参数错误。。。');
}
}
var solider = RoleFactory('法师');
这样一个简单工厂模式就出来了,上面这种是通过对不同的类进行实例化,不过简单工厂模式还可以通过创建对象来实现,我们发现这三个角色函数有一些共同点,因此我们再来通过创建对象来实现
var RoleFactory = function (role) {
var obj = {}
obj.name = role;
if (role == '战士') {
obj.skill = ['刺杀', '十字斩']
} else if (role == '法师') {
obj.skill = ['雷电术', '保护盾']
} else if (role == '道士') {
obj.skill = ['火符', '召唤神兽']
}
return obj;
}
var solider = RoleFactory('法师');
这两个工厂模式还是有一些区别,第一种是通过类来实例对象创建,第二种是通过创建新的对象来包装属性和方法
2、 工厂方法模式
工厂方法模式的本意就是将实际创建对象的工作推迟到子类中,所以我们可以将工厂方法看作是一个实例化对象的工厂类。
在简单工厂模式的第一种方案中,我们添加一个角色就要修改两处代码。安全起见,我们采用安全模式类,我们将创建对象的基类放在工厂方法类的原型中。安全模式类主要解决用户不知道这个类是类,例如:我们知道类前面都是需要用new关键字,如果用户在使用的时候忽略了new关键字直接执行类,此时我们得到的并不是我们预期的对象。
var RoleFactory = function (role) {
if (this instanceof RoleFactory) {
return new this[role]()
} else {
return new RoleFactory(role)
}
}
RoleFactory.prototype = {
Warrior: function () {
this.name = '战士'
this.skill = ['刺杀', '十字斩']
},
Master: function () {
this.name = '法师'
this.skill = ['雷电术', '保护盾']
},
Warlock: function () {
this.name = '道士'
this.skill = ['火符', '召唤神兽']
}
}
var solider = RoleFactory('Master');
现在我们解决了添加一个角色需要修改两处代码,现在我们添加一个角色只需要在RoleFactory.prototype中添加即可
RoleFactory.prototype = {
// ...
Archer: function () {
this.name = '弓箭手'
this.skill = ['百里穿杨', '万箭齐发']
}
}
var solider = RoleFactory('Archer');
3、 抽象工厂模式
抽象工厂模式是通过对类的工厂抽象使其业务用于对产品类簇的创建,而不负责创建某一类产品的实例。跟简单工厂模式创建单一对象,工厂方法模式创建多类对象不一样,抽象工厂模式在javascript中一般不用来创建具体对象。
在抽象工厂中,类簇一般用父类定义,并在父类中定义一些抽象类,在通过抽象工厂让子类继承父类,所以抽象工厂其实是实现子类继承父类的方法
抽象类是一种声明但不能使用的类,当你使用的时候就会报错。
// 弓箭手抽象类,当使用其实例对象的方法时会抛出错误
var Archer = function() {};
Archer.prototype = {
getSkill: function() {
return new Error('抽象方法不能调用')
}
}
我们创建的Archer这个类其实什么都不能做,创建时也没有任何属性,原型上的方法也不能用,否则会报错。但是它在继承上确是有用的,因为定义了一种类,并定义了该类所必备的方法,如果子类中没有重写这些方法,那么在调用时找到这些方法便会报错。
var RoleFactory = function (role, roleType) {
// 判断抽象工厂中是否有该抽象类
if (typeof RoleFactory[roleType] === 'function') {
// 缓存类
function F() {};
// 继承父类属性和方法
F.prototype = new RoleFactory[roleType]();
// 将子类constructor指向子类
role.constructor = role;
// 子类原型继承父类
role.prototype = new F()
} else {
//抛出错误,不存在该抽象类
throw new Error('错误,未创建该抽象类')
}
}
RoleFactory.Warrior = function () {
this.name = '战士'
// this.skill = ['刺杀', '十字斩']
}
RoleFactory.Warrior.prototype = {
getSkill: function () {
return new Error('抽象方法不能调用')
}
}
RoleFactory.Master = function () {
this.name = '法师'
//this.skill = ['雷电术', '保护盾']
}
RoleFactory.Master.prototype = {
getSkill: function () {
return new Error('抽象方法不能调用')
}
}
RoleFactory.Warlock = function () {
this.name = '道士'
//this.skill = ['火符', '召唤神兽']
}
RoleFactory.Warlock.prototype = {
getSkill: function () {
return new Error('抽象方法不能调用')
}
}
// 剑士子类
var Swordsman = function (name, skill) {
this.name = name;
this.skill = skill;
}
// 抽象工厂实现对Swordsman抽象类的继承
RoleFactory(Swordsman, 'Warrior')
Swordsman.prototype.getSkill = function () {
return this.skill
}
var solider = new Swordsman('鬼剑士', '上挑')
console.log(solider.getSkill()) // 上挑