工厂方法模式

在现实生活中社会分工越来越细,越来越专业化。各种产品有专门的工厂生产,彻底告别了自给自足的小农经济时代,这大大缩短了产品的生产周期,提高了生产效率。同样,在软件开发中能否做到软件对象的生产和使用相分离呢?能否在满足“开闭原则”的前提下,客户随意增删或改变对软件相关对象的使用呢?这就是本节要讨论的问题。
在《简单工厂模式》一节我们介绍了简单工厂模式,提到了简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
  • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点:

  • 类的个数容易过多,增加复杂度
  • 增加了系统的抽象性和理解难度
  • 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。

应用场景:

  • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
  • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
  • 客户不关心创建产品的细节,只关心产品的品牌

定义

工厂方法模式,通过对产品类的抽象使其创建业务,主要负责创建多类产品的实例。
工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
可以把这个工厂模式理解成是 进化版的简单工厂模式

实际案例:

比如去旁边还有个专门卖键盘的和耳机的然后他和卖鼠标的组成了一个门店
你要进如这个门店然后买鼠标或者键盘或者耳机
安全模式类:
即使不使用new关键字,也能正确创建出该类的实例的类。
安全模式类,既可以使用new关键字来创建对象,也可以不使用new关键字来创建对象。
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
语法:object instanceof constructor
参数:object(要检测的对象.)constructor(某个构造函数)
描述:instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
// 安全模式类
var Demo = function(){
if(!(this instanceof Demo)){
return new Demo();
};
this.info = '安全类';
};
Demo.prototype = {
name: '安全模式类'
};

// 测试
// 安全模式类,既可以使用new关键字来创建对象,也可以不使用new关键字来创建对象。
var d1 = Demo();
console.log(d1.info);
console.log(d1.name)

var d2 = new Demo();
console.log(d2.info)
console.log(d2.name);
简单工厂 VS. 工厂方法
简单工厂模式,适用于只有几个简单的基类时。
工厂方法模式,适用于基类数量不确定、并且基类较多时。工厂方法模式正适用于这种同时创建多类的实例。
例子:
/*
工厂方法模式--> 即 安全的工厂方法模式
*/

// 用安全模式创建的工厂类
var Factory = function(type,content){
if(this instanceof Factory){
var s = new this[type+'Ads'](content); // 关键点
return s;
}else{
return new Factory(type,content);
};
};

// 工厂原型中设置创建所有类型数据对象的基类
Factory.prototype = {
JavaAds: function(content){
this.content = content;
(function(content){ //自执行的业务逻辑
console.log(content);
})(content);
},
JavaScriptAds: function(content){
this.content = content;
(function(content){
console.log(content);
})(content);
},
UIAds: function(content){
this.content = content;
(function(content){
console.log(content);
})(content);
},
phpAds: function(content){
this.content = content;
(function(content){
console.log(content);
})(content);
},
PythonAds: function(content){
this.content = content;
(function(content){
console.log(content);
})(content);
}
};


// 测试

// 广告数据源
var data = [
{type:'JavaScript',content:'我是javascript广告2'},
{type:'Java',content:'我是Java广告'},
{type:'Php',content:'我是Php广告'},
{type:'UI',content:'我是UI广告'},
{type:'Python',content:'我是Python广告'},
{type:'JavaScript',content:'我是javascript广告2'}
];
// 一键处理广告
for(var i=data.length-1; i>=0; i--){
Factory(data[i].type, data[i].content); // 不加new,可以创建对象
new Factory(data[i].type, data[i].content); // 加new,也可以创建对象
};


总结:

工厂方法模式本意是将实际创建对象工作推迟到子类当中,这样核心类就成了抽象类(不能实例化),但是JavaScript没有像传统创建类那样的方式轻易创建抽象类,所以在JavaScript中实现工厂方法模式只需要参考其核心思想即可。可以将工厂方法看作是一个实例化对象的工厂类,安全起见,采用安全模式类,将创建对象的基类放在工厂方法类的原型中即可。


链接: https://www.fly63.com/course/27_1261