定义
工厂模式定义创建对象的接口,但是让子类决定实例化哪个类。根据不同的输入返回不同类的实例,一般用来创建同一类对象。工厂方式的主要思想是将对象的创建与对象的实现分离,将类的实例化延迟到子类。
概述
我们可以用Object构造函数来创建单个对象。但是,使用同一接口创建很多对象时,会产生大量重复的代码。为了解决这个问题,可以使用工厂模式。
优缺点
工厂模式将对象的创建和实现分离,这带来了优点:
- 良好的封装,代码结构清晰,访问者无需知道对象的创建流程,特别是创建比较复杂的情况下;
- 扩展性优良,通过工厂方法隔离了用户和创建流程隔离,符合开放封闭原则;
- 解耦了高层逻辑和底层产品类,符合最少知识原则,不需要的就不要去交流;
工厂模式的缺点:带来了额外的系统复杂度,增加了抽象性;
例子
function YuXiangRouSi() { this.type = ‘鱼香肉丝‘ } YuXiangRouSi.prototype.eat = function () { console.log(this.type + ‘香喷喷。。。‘) } function GongBaoJiDing() { this.type = ‘宫保鸡丁‘ } GongBaoJiDing.prototype.eat = function () { console.log(this.type + ‘好辣。。。‘) } // 工厂:饭店,根据不同类型生产不同菜品实例 function restaurant(type) { switch (type) { case ‘鱼香肉丝‘: return new YuXiangRouSi() case ‘宫保鸡丁‘: return new GongBaoJiDing() default: return new Error("没有这个菜品") } } const diancan1 = restaurant(‘鱼香肉丝‘); const diancan2 = restaurant(‘宫保鸡丁‘); const diancan3 = restaurant(‘小炒回锅肉‘); diancan1.eat() diancan2.eat()
这样就完成了一个工厂模式,但是这个实现有一个问题:工厂方法中包含了很多与创建产品相关的过程,如果产品种类很多的话,这个工厂方法中就会罗列很多产品的创建逻辑,每次新增或删除产品种类,不仅要增加产品类,还需要对应修改在工厂方法,违反了开闭原则,也导致这个工厂方法变得臃肿、高耦合。
严格上这种实现在面向对象语言中叫做简单工厂模式。适用于产品种类比较少,创建逻辑不复杂的时候使用。
工厂模式的本意是将实际创建对象的过程推迟到子类中,一般用抽象类来作为父类,创建过程由抽象类的子类来具体实现。JavaScript 中没有抽象类,所以我们可以简单地将工厂模式看做是一个实例化对象的工厂类即可。
注意,由于 JavaScript 的灵活,简单工厂模式返回的产品对象不一定非要是类实例,也可以是字面量形式的对象,所以读者可以根据场景灵活选择返回的产品对象形式。
优化:
// 工厂:饭店,产品:菜品,通过菜谱menu,创建不一样的菜品实例 class Restaurant { constructor() { //菜谱,不同菜单,有不同的特色口味,材料颜色口味各不同 this.menuData = {} //‘YuXiangRouSi’: {cailiao: [‘‘,‘‘,‘‘],color:‘‘,eat(){}} 对应 class Menu } // 饭店管理新增菜谱 addMenu(menu, type, message) { if (this.menuData[menu]) { console.log(‘已经有这个菜谱栏‘) return } this.menuData[menu] = {type, message} } //客户点菜,饭店炒菜(实例化菜谱) getMenu(menu) { if (!this.menuData[menu]) { return new Error(‘没有这个菜单‘) } const { type, message } = this.menuData[menu] return new Menu(type, message) } //饭店移除不要的菜谱 removeMenu(menu) { if (!this.menuData[menu]) return delete this.menuData[menu] } } class Menu { constructor(type, message) { this.type = type this.message = message } eat() { console.log(this.type + ‘,‘ + this.message) } } const restaurantFactory = new Restaurant(); restaurantFactory.addMenu(‘YuXiangRouSi‘,‘鱼香肉丝‘,‘好香‘) restaurantFactory.addMenu(‘GaoBaoJiDing‘, ‘宫保鸡丁‘, ‘好辣‘) restaurantFactory.getMenu(‘GaoBaoJiDing‘).eat()
框架中的工厂模式
jQuery中的$()其实就是一个工厂函数,它根据传入参数的不同创建元素或者去寻找上下文中的元素,创建成相应的jQuery对象。
init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if ( typeof selector === "string" ) { //... // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { //.... // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { //.... } return jQuery.makeArray( selector, this ); };
总结
当需要根据不同参数产生不同实例,这些实例都有相同的行为时,可以使用工厂模式。简化实现的过程,同时也可以减少每种对象所需的代码量。工厂模式有利于消除对象间的耦合,提供更大的灵活性。
注:如果不需要另外一个类,或者不需要在运行期间判断实例化的对象属于哪个类,那就不需要使用工厂模式,大多数情况下使用new关键字和构造函数公开实例化对象,提高代码可读性。