设计模式梳理之工厂模式

  • Post author:
  • Post category:其他


工厂模式是一种用来创建对象的设计模式。我们不暴露对象创建的逻辑,而是将逻辑封装在一个函数内,那么这个函数可以成为工厂。工厂模式根据抽象程度的不同可以分为:1.简单工厂 2.工厂方法 3.抽象工厂



简单工厂

简单工厂模式十分好理解,它属于类的创建型模式,又叫静态工厂方法模式。通过专门定义一个工厂类来负责创建其他类的实例,被创建的实例通常都具有共同的父类,也就是说创建出来的示例全都是此构造函数实例化出来的

function bookShop () {
  var book = new Object();
  book.price = '暂无标价';
  if (name === 'JS编程') {
    book.price = '10';
    book.bookFunctio=()=>{}
  }
  if (name === 'vue.3.0') {
    book.price = '20';
    book.bookFunctio=()=>{}
  }
  if (name === '大数据可视化') {
    book.price = '30';
    book.bookFunctio=()=>{}
  }
  return book;
}
var book1 = bookShop('JS编程');
var book2 = bookShop('vue.3.0');
var book3 = bookShop('大数据可视化');

拿到的数据肯定都是bookShop构造的示例,实例中有不同的price,和不同的函数,这就是简单的工厂模式,也是最最基础的工厂模式


优点



工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。

客户端无需知道所创建具体产品的类名,只需知道参数即可。

也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。


缺点



简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。

使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度

系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂 简单工厂模式使用了 static工厂方法,造成工厂角色无法形成基于继承的等级结构。



工厂方法模式

工厂方法模式中,核心的工厂类不再负责所以产品的创建,而是将具体的创建工作交给子工厂来做,这一点是网上有些例子讲的不是太清楚,在普遍意义的编程语言中,这个的核心就是

继承

,用子工厂

继承

父工厂的属性,且实例化的时候

实例化子工厂

,产生的实例也是不同子工厂的实例,但是前端中的

class

实际上是语法糖,我们也可以按照继承来写,但是我看到的是我们可以把子工厂放

prototype

上,这样工厂来生产对象的时候从表面上来看还是从父工厂来产生的,与prototype原型链有关的暂时不做赘述

var BookShop = function (name) {
  // 如果外部直接调用了BookShop而不是new关键字调用,则返回new BookShop调用,否则直接调用
  // 这个产品类创建实例返给外部
  //if (this instanceof BookShop) {
    var bookObj = new this[name]()
    return bookObj
 // } else {
 //   return new BookShop(name)
 // }
}
BookShop.prototype = {
  Programme: function () {
    this.books = ['css世界', 'JS高级编程', 'ES6入门教程']
  },
  //Science: function () {
  //  this.books = ['人与自然', '大自然的奥秘', '走进科学']
  //},
  //Society: function () {
  //  this.books = ['精神小伙修炼手册', '摇花手', '豆豆鞋']
  //}
}
var programme = new BookShop('programme');
//var science = BookShop('science');
//var society = BookShop('society');


如果外部直接调用了BookShop而不是new关键字调用,则返回new BookShop调用,否则直接调用

,这句话,跟工厂方法模式没什么关系,主要是兼容了工厂的实例化没有用

new

关键字,我注释掉了重复或者跟工厂方法模式本身无关的地方,只看最简单的

1.

子工厂的挂载

,

2.父工厂通过接收参数方式来使用不同的

子工厂创建实例

,得到了不同的构造函数所创造的实例,这就是工厂方法模式,new的都是同一个构造函数,得到的却不是相同的实例


优点



拥有工厂方法的优点

可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。

当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。

抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。


缺点


当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。



抽象工厂模式

抽象工厂模式相对来说比较复杂,想要了解抽象工厂模式,必须得知道什么是抽象类



抽象类
  • 什么是抽象类,就是在父类中调用一个未定义的方法,这个方法在子类中必须被实现。
  • 什么是抽象方法,就是一个已定义但是没有实现的方法,这个东西如果有计算机基础的人会知道,在c++和java中有

    接口

    ,比较类似
Object.extend = function(des,source){
    for(p in source){
        des[p] = source[p];
    }
    return des;
};
Object.prototype.extend = function(object){
    return Object.extend.apply(this,[this,object]);
};

function BaseClass(){};
BaseClass.prototype = {
    initialize:function(name,age){
        this.name = name;
        this.age = age;//调用了一个抽象方法
        this.oninit();
    },
    //抽象方法是一个空方法,由派生类实现
    oninit:function(){} 
};
function ClassA(){};
ClassA.prototype = (new BaseClass()).extend({
    oninit:function(){
        alert(this.name + ' : ' + this.age);
    }
}); 
var obj = new ClassA();
obj.initialize('Tom',22);

如果子类不是抽象类的话,那么在子类中必须重写抽象父类的抽象方法,简单来讲就是不管他是啥,我先定义好,谁想来继承,就要把这个方法给实现了



抽象工厂
  • 核心问题,在于继承,抽象工厂的能力就在于不同的工厂去继承抽象工厂
let agency = function(subType, superType) {
  //判断抽象工厂中是否有该抽象类
  if(typeof agency[superType] === 'function') {
    function F() {};
    //继承父类属性和方法
    F.prototype = new agency[superType] ();
    //将子类的constructor指向子类
    subType.constructor = subType;
    //子类原型继承父类
    subType.prototype = new F();

  } else {
    throw new Error('抽象类不存在!')
  }
}

如上:拥有继承抽象类的能力,叫抽象工厂

代码也比较容易理解,通俗的来讲就是,把父类的一身本领全部挂载到一个空的类的prototype上,然后把子类的constructor指向子类,然后再把挂载到空的类的一身本领挂载给子类,这样子类就从父类那里学到了父类的一身本领

agency.mouseShop = function() {
  this.type = '鼠标';
}
agency.mouseShop.prototype = {
  getName: function() {
    return new Error('抽象方法不能调用');
  }
}

如上:创建一个抽象类等待继承

function mouse(name) {
  this.name = name;
  this.item = ['买我,我线长',"玩游戏贼溜"]
}
//抽象工厂实现鼠标类的继承
agency(mouse, 'mouseShop');
//子类中重写抽象方法
mouse.prototype.getName = function() {
  return this.name;
}

如上:普通类继承抽象子类,并重写抽象子类的方法

//实例化普通类
let mouseA = new mouse('联想');
console.log(mouseA.getName(), mouseA.type); //联想 鼠标

如上:实例化普通类,使用类的方法及属性

总结:

1.前两种工厂看起来十分简单,但是抽象工厂理念对于有些人可能难以理解,为啥要创建一个这样的工厂,其实简单的例子不能看出来工厂的方便之处,但是我们遐想一下,如果相同之处非常多,或者说多种类多种工厂,有很多的相似之处,那我们一个总工厂的必要性就出来了,多个普通类都可以更简便的书写,因为他们的共同之处都在

子抽象类

中已经命名定义好了,我们继承之后之专注自己的逻辑以及代码就好

2.这是一种思维,并不是一种照搬的开发模式,工厂的思维在某些情境下是十分有用的,特别是较为复杂较为繁琐的地方,能让我们的开发逻辑更清晰明白,也更好分析代码逻辑,

不是啥时候都适合使用工厂模式的嗷

,因地制宜因材施教



版权声明:本文为weixin_44403976原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。