1.工厂模式
工厂模式是用来创建对象的一种最常用的设计模式。在不暴露创建对象的具体逻辑下,将逻辑封装在一个函数中,这个函数就可以被视为一个工厂。工厂模式根据抽象程度的不同可以分为:简单工厂,工厂方法和抽象工厂。
如果只了解JavaScript的人也许不能很好学的理解抽象这个词,因为JavaScript一直把abstract作为保留字并没有被实现,所以就很难理解工厂模式中的三种方法的异同。所以,我们先通过一个简单的例子讲述一下抽象和工厂的概念。
比如说:我要去一个大型的体育用品店买一个篮球,但是店里除了篮球以外还有足球、网球等等其他各种各样的体育用品,由于第一次来到这家店,对店内商品摆放位置不熟悉,短时间内想要找到它可能有点困难,于是我找到售货员,并告诉他我想要买个球,然后他问我:“你要买什么类型的球”,我说我要买一个7号的篮球,在售货员的帮助下我很快就找到了想要的篮球。
上面例子中你最后买到的篮球可以被看作是一个实例对象,体育用品店可以看作是一个工厂,我们可以认为他是一个函数,这个工厂函数中有着各种各样的体育用品,那么你是如何获取到实例的呢?因为我们给体育用品店传递了正确的参数---“篮球”。
在第一次告诉售货员我的需求时,我只说了想要买一个球,他不能明白我到底想要买什么类型的球,因为我说得太抽象。我回答的球分为很多种类,而没有具体到某一商品,这种将复杂事物的一个或多个共有特征抽取出来的思维过程就是抽象。
既然已经明白了抽象的概念,下面我们来看一下之前提到的工厂模式的三种实现方法: 简单工厂模式、工厂方法模式、抽象工厂模式。今天主要学习简单工厂和工厂方法。
1.1简单工厂模式
简单工厂模式又称为静态工厂模式,由一个工厂对象决定去创建某一种产品对象类的实例。主要用于创建同一类的对象。
根据体育用品店实例化球类的例子。代码如下:
//篮球基类
var Basketball = function(){
this.intro = '篮球盛行于美国';
}
Basketball.prototype = {
getMember : function(){
console.log("每个队伍需要5名球员");
}
}
//足球基类
var Football= function(){
this.intro = '足球在世界上很受欢迎';
}
Football.prototype = {
getMember : function(){
console.log("每个队伍需要11名球员");
}
}
//网球基类
var Tennis = function(){
this.intro = '李娜在中国网球界是顶尖选手';
}
Tennis .prototype = {
getMember : function(){
console.log("每个队伍需要1名球员");
}
}
//运动工厂
var SportsFactory = function(name){
switch(name){
case 'NBA':
return new Basketball();
case 'wordCup':
return new Football();
case 'frenchOpen':
return new Tennis();
}
}
var basketball = sportsFactory('NBA');
console.log(basketball);
console.log(basketball.intro);
basketball.getMember();
SportsFactory
就是一个简单工厂,在该函数中有3个构造函数分别对应不同的体育用品的类。当我们调用工厂函数时,只需要传递NBA
, wordCup
,frenchOpen
这三个可选参数中的一个获取对应的实例对象。
1.2 工厂方法模式
工厂方法模式的本意是将实际创建对象的工作推迟到子类中,这样核心类就变成了抽象类。但是在JavaScript中很难像传统面向对象那样去实现创建抽象类。所以在JavaScript中我们只需要参考它的核心思想即可。我们可以将工厂方法看作是一个实例化对象的工厂类。
在简单工厂模式中,我们每添加一个构造函数需要修改两处代码。现在我们使用工厂方法模式改造上面的代码,刚才提到,工厂方法我们只把它看作是一个实例化对象的工厂,它只做实例化对象这一件事情! 我们采用安全模式创建对象。
//安全模式创建的工厂方法函数
var SportsFactory = function(name){
if(this instanceof SportsFactory ) {
var s = new this[name]();
return s;
} else {
return new SportsFactory (name);
}
}
//工厂方法函数的原型中设置所有对象的构造函数
SportsFactory.prototype = {
Basketball: function() {
this.intro= "篮球盛行于美国"
},
Football: function() {
this.intro = '足球在世界上很受欢迎';
}
Tennis : function() {
this.intro = '李娜在中国网球界是顶尖选手';
}
}
//调用
var basketball = sportsFactory('Basketball');
var football= sportsFactory('Football');
var tennis= sportsFactory('Tennis');
上面的这段代码就很好的解决了每添加一个构造函数就需要修改两处代码的问题,如果我们需要添加新的角色,只需要在SportsFactory.prototype
中添加。例如,我们需要添加一个Pingpang
:
SportsFactory.prototype = {
//....
Pingpang: function() {
this.intro= '中国在乒乓球领域是世界霸主!',
}
}
//调用
var pingpang= SportsFactory('Pingpang');
上面的这段代码中,使用到的安全模式可能很难一次就能理解
var SportsFactory = function(name) {
if(this instanceof SportsFactory ) {
var s = new this[name]();
return s;
} else {
return new SportsFactory (name);
}
}
因为我们将Basketball
、Football
、Tennis
等构造函数保存到了SportsFactory.prototype
中,也就意味着我们必须实例化SportsFactory
函数才能够进行以上对象的实例化。如下面代码所示。
var SportsFactory = function() {}
SportsFactory .prototype = {
//...
}
//调用
vat factory = new SportsFactory ();
let basketball= new factory.basketball();
在上面的调用函数的过程中, 一旦我们在任何阶段忘记使用new
, 那么就无法正确获取到basketball
这个对象。但是一旦使用安全模式去进行实例化,就能很好解决上面的问题。
总结
简单工厂模式又叫静态工厂方法,用来创建某一种产品对象的实例,用来创建单一对象;工厂方法模式是将创建实例推迟到子类中进行。在实际的业务中,需要根据实际的业务复杂度来选择合适的模式。对于非大型的前端应用来说,灵活使用简单工厂其实就能解决大部分问题。