工厂方法模式(Factory Method):通过对产品类的抽象使其创建业务主要负责用于创建多类产品的实例。
广告是一个公司主要的一个经济来源,这不,很多企业等着要来公司首页打广告呢。
广告展现
“小白, 咱们新来了一批广告资源需要投放, 关于计算机培训的。一批是 Java的, 用绿色字体。还有一批是PHP的,要用黄色字体,红色背景。”
“没问题, 于是小白准备创建两个类, 然后通过实例对象方式来完成这个需求”。
//创建Java学科类
var Java = function(content){
//将内容保存在content里面以备日后使用
this.content = content;
//创建对象时,通过闭包,直接执行,将内容按需求的样式插入到页面
(function(content){
var div = document.createElement('div');
div.innerHTML = content;
div.style.color = 'green';
document.getElementById("container").appendChild(div);
})(content);
}
//创建PHP学科类
var Php = function(){
this.content = content;
(function(content){
var div = document.createElement('div');
div.innerHTML = content;
div.style.color = 'yellow';
div.style.background = 'red';
document.getElementById("container").appendChild(div);
})(content);
}
刚写完就听到身后的喊声:“小白,又来了一批广告,关于 JavaScript的,要求背景色粉色……”
好吧,”突然间小白想起昨天学习的简单工厂模式,心想,“正好派上用场了,就用简单工厂模式去实现吧, 这样日后再创建对象直接找工厂就好了 。”
//创建Java学科类
var Java = function(content){
..........
}
//创建PHP学科类
var Php = function(content){
..........
}
//创建JavaScript学科
var JavaScript = function(content){
this.content = content;
(function(content){
var div = document.createElement('div');
div.innerHTML = content;
div.style.background = 'pink';
document.getElementById("container").appendChild(div);
})(content);
}
//学科类工厂
function JobFactory(type, content){
switch(type){
case 'java' :
return new Java(content);
case 'php' :
return new Php(content);
case 'JavaScript' :
return new JavaScript(content);
}
}
然后写了一个测试案例:
JobFactory('JavaScript', 'JavaScript哪家强');
“小白, 又来了一批uI学科, 红色边框……”。
小白沉默了…
方案的抉择
小铭见状走了过来。“怎么了,小白?”
“需求总在变,我不知道哪种解决方式更好,开始需求简单,我就直接创建对象,后来需求多了,我就用简单工厂方法重构,而现在又变了,我不仅仅要添加类,还要修改工厂函数。兩我更担心的是未来的需求还会变……””
“这样呀,”小铭微笑一下,”不用担心,需求变是正常的,而我们还有更好的模式可以应用, 刚才你用简单工厂模式遇到的问题就是每添加一个类就要修改两处是吧, 所以说你可以试用一下工厂方法模式 。这样以后每需要一个类,你只需要添加这个类就行,其他的不用操心了 。”
“工厂方法?这是一个什么样的模式?”
“工厂方法模式本意是说将实际创建对象工作推迟到子类当中。这样核心类就成了抽象类, 不过对于 JavaScript不必这么深究, JavaScript没有像传统创建抽象类那样的方式轻易创建抽象类, 所以在 JavaScript中实现工厂方法模式我们只需要参考它的核心思想即可 。 所以我们可以将工厂方法看作是一个实例化对象的工厂类。安全起见,我们釆用安全模式类,而我们将创建对象的基类放在工厂方法类的原型中即可 。”
安全模式类
小白有些不懂,打断小铭的话:“小铭,什么叫安全模式类?你说的我不是很懂,你能详细说明一下么? ”
“安全模式类是说可以屏蔽使用这对类的错误使用造成的错误。 比如对于一个类 (我们暂且称之为 Demo类)的创建,我们知道类的前面是需要有 new关键字的(如 var d= new Demo()),不过如果其他人不知道这个对象(Demo)是一个类, 那么在使用时很可能忽略 new关键字直接执行类(如 var d = Demo();),此时我们得到的并不是我们预期的对象,如下所示
var Demo = function(){};
Demo.prototype = {
show : function(){
console.log('成功获取!');
}
}
var d = new Demo();
d.show();//成功获取
var d = Demo();
d.show();//Uncaught TypeError: Cannot read property 'show' of undefined
“那么你所说的安全模式就是为了解决这种问题吧 。”
“当然, 这也是避免像你一样的那些新来同学可能犯的错误。 当然解决方案很简单, 就是在构造函数开始时先判断当前对象this指代是不是类(Demo),如果是则通过 new关键字创建对象, 如果不是说明类在全局作用域中执行 (通常情况下) , 那么既然在全局作用域中执行当然this就会指向 window了(通常情况下,如非浏览器环境可为其他全局对象),这样我们就要重新返回新创建的对象了 。”
var Demo = function(){
if(!(this instanceof Demo)){
return new Demo();
}
}
Demo.prototype = {
show : function(){
console.log('成功获取!');
}
}
var d = Demo();
d.show();//成功获取
“有了安全模式我们就可以将这种技术应用在我们的工厂方法中了 。”
安全的工厂方法
//安全模式创建的工厂类
var Factory = function(type, content){
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){
.....
},
UI : function(content){
this.content = content;
(function(content){
var div = document.createElement('div');
div.innerHTML = content;
div.style.border = '1px solid red';
document.getElementById('container').appendChild(div);
})
},
php : function(content){
.....
}
};
“这样我们以后如果想添加其他类时,是不是只需写在 Factory 这个工厂类的原型里面就可以了?”
“嗯,是这样,你再也不必担心创建时做任何修改。就好比你在 Factory 类的原型里面注册了一张名片, 以后需要哪类直接拿着这张名片, 査找上面的信息就能找到这个类了, 所以你就不用担心使用时找不到基类的问题了。”
“小白。这里是我们今天要添加广告的数据,给你,现在就给添加上吧。”经理走过来对小白说。
var data = [
{type:'JavaScript', content:'JavaScript哪家强'},
{type:'Java', content:'Java哪家强'},
{type:'Php', content:'Php哪家强'},
{type:'UI', content:'UI哪家强'},
{type:'Java', content:'Java哪家强'},
];
小白接过数据一看, 格式很友好。 于是很快完成了经理提出的需求。
for(var i = 6; i >=0; i--){
Factory(data[i].type, data[i].content);
}
“小白,广告那边又来了需求,需要一批C++学科,蓝色字体……”
小白听到,笑了笑。。。。
回忆
对于创建多类对象,前面学过的简单工厂模式就不太适用了,这是简单工厂模式的应用局限, 当然这正是工厂方法模式的价值之所在, 通过工厂方法模式我们可以轻松创建多个类的实例对象, 这样工J一方法对象在创建对象的方式也避免了使用者与对象类之间的耦合, 用户不必关心创建该对象的具体类, 只需调用工厂方法即可。在实践中, 理想与现实总有一线之隔的, 新来的同事很可能错误地直接调用工厂方法, 这样就很有可能通过工厂方法执行中的 this对象为全局对象添加全局变量,并且得不到期望的实样就很有可能通过工厂方法执行中的 this对象为全局对象添加全局变量,并且得不到期望的实例对象, 此时一个安全的工厂对象则让我们吃了一颗定心丸 。