【设计模式】我这样学习设计模式-工厂模式

1,325 阅读9分钟

工厂模式

工厂模式是用来创建对象的一种最常用的设计模式。根据抽象程度的不同可以分为:简单工厂模式工厂方法模式抽象工厂模式

简单工厂模式

定义

简单工厂模式,又称为静态工厂方法模式。由一个工厂对象决定创建出哪一种产品类的实例。

类图

我决定不干前端了,回家开了个星巴克咖啡厅,顾客到我的咖啡厅点一杯咖啡,告诉我需要哪种咖啡,我来根据顾客的决定创建咖啡。由于是小本生意,全部由我亲力亲为。

微信截图_20210617171928.png

咖啡类:Coffee

子类:美式咖啡(AmericanCoffee)、拿铁咖啡(LatteCoffee)、卡布奇诺咖啡(CappuccinoCoffee)

代码

//抽象类只能用于继承  咖啡类
abstract class Coffee {
  /*
  在参数中加public修饰符可以达到简写的目的
  不用声明name:string
  构造函数中不用this.name=name
  */
  constructor(public name: string) {}
}

//子类  美式咖啡
class AmericanCoffee extends Coffee {}
//子类  拿铁咖啡
class LatteCoffee extends Coffee {}
//子类  卡布奇诺咖啡
class CappuccinoCoffee extends Coffee {}

//建立咖啡工厂  简单工厂  根据传入的参数返回不同子类的实例
class CafeFactory {
  static order(name: string) {
    switch (name) {
      case 'American':
        return new AmericanCoffee('美式咖啡');
      case 'Latte':
        return new LatteCoffee('拿铁咖啡');
      case 'Cappuccino':
        return new CappuccinoCoffee('卡布奇诺咖啡');
      default:
        throw new Error('没有此种咖啡');
    }
  }
}

//调用工厂函数测试一下
console.log(CafeFactory.order('American')); //AmericanCoffee {name: "美式咖啡"}
console.log(CafeFactory.order('Latte')); //LatteCoffee {name: "拿铁咖啡"}
console.log(CafeFactory.order('Cappuccino')); //CappuccinoCoffee {name: "卡布奇诺咖啡"}
console.log(CafeFactory.order('meinianda')); //Uncaught Error: 没有此种咖啡

这就是一个简单工厂,根据传入的参数,返回不同子类的实例。

优点

只需要一个正确的参数,就可以获取到你所需要的对象,而无需知道其创建的具体细节。

缺点

如果产品的种类非常多switch case的判断会变得非常多,这个函数就会变的非常臃肿并且难以维护。

函数内包含了所有对象的创建逻辑和判断逻辑的代码,,如果要增加或删除一个产品种类,就需要修改判断逻辑代码,不符合开放—封闭原则

所以,简单工厂只能作用于创建的对象数量较少,对象的创建逻辑不复杂时使用。

示例

jQuery源码中的$(selector)就是一个简单工厂

$('div')new $('div')哪个好用,很显然直接$(div)更方便 ,所以这里使用简单工厂模式,简略了new的过程

jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
	return new jQuery.prototype.init( selector, context );
};

仿照jQuery实现一个简单工厂函数

//联合类型
interface jQuery {
  [index: number]: any;
}
class jQuery {
  length: number;
  constructor(selector: string) {
    //获取dom元素 使用Array.from将类数组转成数组
    let elements = Array.from(document.querySelectorAll(selector));
    let length = elements ? elements.length : 0;
    this.length = length;
    for (let i = 0; i < length; i++) {
      this[i] = elements[i];
    }
  }
  html(htmlText: string | undefined) {
    //如果传参就是赋值,否则就是取值
    if (htmlText) {
      for (let i = 0; i < this.length; i++) {
        this[i].innerHTML = htmlText;
      }
    } else {
      return this[0].innerHTML;
    }
  }
}

interface Window {
  $: any;
}

//简单工厂就是函数里返回类的实例
window.$ = function (selector: string) {
  return new jQuery(selector);
};

工厂方法模式

定义

工厂方法模式,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给工厂子类去做。

工厂方法模式的本意是将实际创建对象的工作推迟到子类中,这样核心类就变成了抽象类。我们可以将工厂方法看作是一个实例化对象的工厂类。

类图

开店就是比干前端挣钱,随着我星巴克咖啡店规模的扩大,品种的增加,并且我的顾客也越来越多,这时我一个人就已经忙不过来了,我决定安心当老板,躺平收钱。于是我顾了几个服务员,每个服务员负责 一个品种 的制作。顾客在我这下单,再由我分配给对应的服务员进行生产。

微信截图_20210622145249.png

与简单工厂的区别

在简单工厂模式中,是由工厂Factory来创建产品的。

在工厂方法模式中,不再由工厂Factory来创建产品,而是先创建具体的工厂,然后由具体的工厂来创建产品。

代码

//Description:工厂方法模式 把创建产品的工作交由具体工厂类来实现

//抽象类只能用于继承  咖啡类
abstract class Coffee {
  constructor(public name: string) {}
}
//子类  美式咖啡
class AmericanCoffee extends Coffee {}
//子类  拿铁咖啡
class LatteCoffee extends Coffee {}
//子类  卡布奇诺咖啡
class CappuccinoCoffee extends Coffee {}

//抽象类  咖啡工厂类
abstract class CafeFactory {
  //抽象方法 不需要实现
  abstract createCoffee(): Coffee;
}

//美式咖啡工厂
class AmericanFactory extends CafeFactory {
  createCoffee() {
    return new AmericanCoffee('美式咖啡');
  }
}

//拿铁咖啡工厂
class LatteFactory extends CafeFactory {
  createCoffee() {
    return new LatteCoffee('拿铁咖啡');
  }
}

//卡布奇诺咖啡工厂
class CappuccinoFactory extends CafeFactory {
  createCoffee() {
    return new CappuccinoCoffee('卡布奇诺咖啡');
  }
}


let americanFactory = new AmericanFactory();
console.log(americanFactory.createCoffee()); //AmericanCoffee {name: "美式咖啡"}

let latteFactory = new LatteFactory();
console.log(latteFactory.createCoffee()); //LatteCoffee {name: "拿铁咖啡"}

let cappuccinoFactory = new CappuccinoFactory();
console.log(cappuccinoFactory.createCoffee()); //CappuccinoCoffee {name: "卡布奇诺咖啡"}

也可以结合简单工厂模式简化代码

//结合简单工厂模式
class Factory {
  static order(name: string) {
    switch (name) {
      case 'American':
        return new AmericanFactory().createCoffee();
      case 'Latte':
        return new LatteFactory().createCoffee();
      case 'Cappuccino':
        return new CappuccinoFactory().createCoffee();
      default:
        throw new Error('没有此种咖啡');
    }
  }
}
//调用工厂函数测试一下
console.log(Factory.order('American')); //AmericanCoffee {name: "美式咖啡"}
console.log(Factory.order('Latte')); //LatteCoffee {name: "拿铁咖啡"}
console.log(Factory.order('Cappuccino')); //CappuccinoCoffee {name: "卡布奇诺咖啡"}
console.log(Factory.order('meinianda')); //Uncaught Error: 没有此种咖啡

优点

工厂方法模式每个具体工厂类只完成单一任务,代码简洁,如果要增加一个产品种类,只需要增加一个产品工厂,符合 开放—封闭原则,有非常良好的 扩展性

缺点

假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦。

每增加一个产品,相应的也要增加一个工厂,会加大额外的开发量。

示例

仿照react源码,一个用来生成dom的工厂方法

function createElement(type: any, config: any) {
  //this绑定为null 第一个参数绑定为type
  return { type, props: config };
}

function createFactory(type) {
  //源码中没有用到this,所以this绑定为null
  const factory = createElement.bind(null, type);
  return factory;
}

let factory = createFactory('h1');
let element = factory({ id: 'h1', className: 'title' });

抽象工厂模式

定义

抽象工厂模式,又称其他工厂的工厂。可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象

先借助一个图来理解产品族和产品等级

我的咖啡厅一共有三种咖啡,美式,拿铁,卡布奇诺。由于我明智的放弃了前端,选择了星巴克,赚了很多钱,这个时候我决定再开一家咖啡厅,名字叫瑞幸。咖啡的品类和星巴克一样。

产品等级:即产品的继承结构,可以理解为不同家的同一产品

产品族:指由同一个工厂生产的,位于不同产品等级结构中的一组产品,可以理解为同一家的不同产品,是一组相关或相互依赖的对象。

abstractfactory2.png

组成角色

抽象工厂: 提供了创建产品的接口,包含多个创建产品的抽象方法(咖啡工厂)

具体工厂: 实现抽象工厂定义的接口,完成某个具体产品的创建(星巴克工厂和瑞幸工厂)

抽象产品:产品的定义,一般有多少抽象产品,抽象工厂中就包含多少个创建产品的方法(美式咖啡,拿铁咖啡,卡布奇诺咖啡)

具体产品: 抽象产品的实现类(星巴克美式咖啡,瑞幸拿铁咖啡)

类图

咖啡工厂(CafeFactory),包含制作三种咖啡的抽象方法,子类星巴克工厂和瑞幸工厂分别生产各自的美式咖啡,拿铁咖啡,卡布奇诺咖啡。

微信截图_20210622184007.png

与工厂方法模式的区别

工厂方法模式针对的是同一类或同等级产品,而抽象工厂模式针对的是多种类的产品设计

代码

//抽象类可以继承抽象类
abstract class Coffee {}

//抽象产品
abstract class AmericanCoffee extends Coffee {}
abstract class LatteCoffee extends Coffee {}
abstract class CappuccinoCoffee extends Coffee {}

//具体产品的个数 =  产品族 * 产品等级
class StarBucksAmericanCoffee extends AmericanCoffee {}
class StarBucksLatteCoffee extends LatteCoffee {}
class StarBucksCappuccinoCoffee extends CappuccinoCoffee {}
class LuckinAmericanCoffee extends AmericanCoffee {}
class LuckinLatteCoffee extends LatteCoffee {}
class LuckinCappuccinoCoffee extends CappuccinoCoffee {}

//抽象工厂 需要三个抽象方法
abstract class CafeFactory {
  //抽象方法  创建美式咖啡
  abstract createAmericanCoffee(): AmericanCoffee;
  //抽象方法  创建拿铁咖啡
  abstract createLatteCoffee(): LatteCoffee;
  //抽象方法  创建卡布奇诺咖啡
  abstract createCappuccinoCoffee(): CappuccinoCoffee;
}

//具体工厂 星巴克
class StarBucksCafeFactory extends CafeFactory {
  //具体方法  创建星巴克美式咖啡
  createAmericanCoffee() {
    return new StarBucksAmericanCoffee();
  }
  //具体方法  创建星巴克拿铁咖啡
  createLatteCoffee() {
    return new StarBucksLatteCoffee();
  }
  //具体方法  创建星巴克卡布奇诺咖啡
  createCappuccinoCoffee() {
    return new StarBucksCappuccinoCoffee();
  }
}

//具体工厂 瑞幸
class LuckinCafeFactory extends CafeFactory {
  //具体方法  创建星巴克美式咖啡
  createAmericanCoffee() {
    return new LuckinAmericanCoffee();
  }
  //具体方法  创建星巴克拿铁咖啡
  createLatteCoffee() {
    return new LuckinLatteCoffee();
  }
  //具体方法  创建星巴克卡布奇诺咖啡
  createCappuccinoCoffee() {
    return new LuckinCappuccinoCoffee();
  }
}

//创建瑞幸工厂
let luckinCafeFactory = new LuckinCafeFactory();
//创建瑞幸的美式咖啡
console.log(luckinCafeFactory.createAmericanCoffee()); //LuckinAmericanCoffee {}

优点

当系统需要新增一个产品族时,只需要增加新的工厂类即可,无需修改源代码;

缺点

但是如果需要产品族中增加一个新种类的产品时,则所有的工厂类都需要修改

总结

无论是 简单工厂模式工厂方法模式 还是 抽象工厂模式,它们本质上都是将不变的部分提取出来,将可变的部分留作接口,以达到最大程度上的 复用。究竟用哪种设计模式更适合,这要根据具体的业务需求来决定。不要学会了用刀,就见什么都砍。

小伙伴们觉的对你有帮助的请点赞👍👍支持一下,感觉写的不错的请关注一下专栏

👉👉适合前端人员的设计模式

参考

web前端-js设计模式

三大工厂模式的优缺点