JavaScript工厂模式
1.简单工厂模式
简单工厂模式的概念就是创建对象,将我们需要使用的方法封装在一个函数中,通过这个函数调用并创建新的方法的实例,也就是说我们不需要显式的new一个新的实例,工厂模式自动返回新的实例化对象。
//假设有一个大型奥运会比赛,通过工厂模式将其封装在奥运会的工厂模式中
function OlympicsFactory(sports){
switch(sports){
case 'Basketball':
return new Basketball();
case 'Football':
return new Football();
case 'Swim':
return new Swimming();
}
}
//创建具体的运动类型,主要用于表示不同的类型的方法用于干什么的
var Basketball = function(){
console.log("This is BasketBall Sports");
}
var Football = function(){
console.log("This is Football Sports");
}
var Swim = function(){
console.log("This is Swimming Sports");
}
//调用方法
OlympicsFactory(Football);
分析:使用上面的方式有一个缺点:当上面运动的方法中有相同的属性时,就会创建相同的属性代码,比如运动会的时间,地点以及参与人数等,这会造成程序的冗余度较高。这时我们可以在工厂函数中封装相同的属性代码。
function OlympicsFactory(sports,location,time,participants){
var o = new Object();
//相同部分
o.time = time;
o.location = location;
o.participants = participants;
o.getDetails = function(){
console.log("This is" + sports + "Sports");
}
//不同部分
if(sports == 'Basketball'){
o.属性 = 属性值;
}
if(sports == 'Football'){
o.属性 = 属性值;
}
if(sports == 'Swimming'){
o.属性 = 属性值;
}
return o;
}
//调用工厂函数返回实例化对象
var Basketball = OlympicsFactory("Basketball", "Brasil", "2016", "5000");
两种方式根据实际情况使用,如果项目中没有共有属性和方法,第一种最好,如果项目中共有方法属性过多,第二种方法最适合。
更安全的简单工厂模式(也叫工厂方法模式《JavaScript设计模式》里的概念):在以上第一种方法中,如果要添加一项新的运动,如跳水,我们需要在工厂函数中创建diving的case,也需要在外部创建具体的运动类型,这样每添加一个类就要修改两个地方,多一个操作就会多一分危险,而且我们创建的工厂函数是被封装好了的,其他的开发人员并不知道我们创建工厂函数使用的是funciton关键字还是new操作符,所以他们在调用此工厂方法时可能会用new关键字实例化一个新的工厂方法模式。所以改进代码如下:
//判断this的指向,如果this指向OlympicsFactory,直接调用即可,如果不是,实例化一个新的OlympicsFactory方法
var OlympicsFactory = function (sports, content){
if(this instanceof OlympicsFactory){
return new this[sports](content);
} else {
return new OlympicsFactory(sports, content);
}
};
//将OlympicsFactory方法的所有内部属性或放阿飞封装在OlympicsFactory方法的原型中,如果需要添加新的sports,只需要在原型中添加就可
OlympicsFactory.prototype = {
Basketball: function(content){
console.log("This is " + content + " Sports");
},
Football: function(content){
console.log("This is " + content + " Sports");
},
Swimming: function(content){
console.log("This is " + content + " Sports");
},
//新增diving跳水运动
Diving: function(content){
console.log("This is " + content + " Sports");
}
};
//调用方法
var a = OlympicsFactory("Football", "exciting");
var b = new OlympicsFactory("Baseketball", "dangering");
2.抽象工厂模式
从简单工厂模式我们不难得出,创建运动类型就就像是继承一样,继承OlympicsFactor函数里的方法。而简单工厂模式在函数内部定义了一个闭包,当我们修改这个闭包,即添加新的实例基类时,需要对这个闭包做出相应的更改,违背了闭包的只读原则,所以抽象工厂模式是一种更优化的设计模式,我们只需要知道要创建的子类和父类的名称,调用抽象工厂方法使子类继承父类的属性和方法。
//type表示运动类型,如球类var OlympicsFactory = function (sports, type){
if(typeof OlympicsFactory[type] == 'function'){
function F(){};
//创建一个F类用于中间变量,用于继承OlympicsFactory的属性和方法,从而将这些属性方法再继承给子类sports
F.prototype = new OlympicsFactory[type]();
sports.constructor = sports;
sports.prototype = new F();
//继承F继承的属性和方法,重写了sports.prototype属性
} else {
throw new Error('出错!');
}
};
//创建Basketball子类,它属于Ball类运动。
var Basketball = function(time, location){
this.time = time;
this.location = location;
}
//调用抽象工厂方法,实现对Ball的继承
OlympicsFactory(Basketball, 'Ball');
Basketball.prototype.getTime = function(){
return this.time;
}
Basketball.prototype.getLocation = function(){
return this.location;
}
//调用
var basketlball = new Basketball("2016", "Brazil");
console.log(basketball.getTime);
抽象工厂模式的好处是当我们想要新创建一个子类时,不必在模式定义的函数内部创建,直接通过继承实现子类所在的父类对象的属性和方法,所以抽象工厂模式返回的不是对象的实例,而是实例所属的类。