工厂模式是用来创建对象的一种最常用的设计模式。根据抽象程度的不同可以分为:简单工厂模式,工厂方法模式 和 抽象工厂模式。
简单工厂模式
定义
简单工厂模式,又称为静态工厂方法模式。由一个工厂对象决定创建出哪一种产品类的实例。
类图
我决定不干前端了,回家开了个星巴克咖啡厅,顾客到我的咖啡厅点一杯咖啡,告诉我需要哪种咖啡,我来根据顾客的决定创建咖啡。由于是小本生意,全部由我亲力亲为。
咖啡类: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);
};
复制代码
工厂方法模式
定义
工厂方法模式,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给工厂子类去做。
工厂方法模式的本意是将实际创建对象的工作推迟到子类中,这样核心类就变成了抽象类。我们可以将工厂方法看作是一个实例化对象的工厂类。
类图
开店就是比干前端挣钱,随着我星巴克咖啡店规模的扩大,品种的增加,并且我的顾客也越来越多,这时我一个人就已经忙不过来了,我决定安心当老板,躺平收钱。于是我顾了几个服务员,每个服务员负责 一个品种 的制作。顾客在我这下单,再由我分配给对应的服务员进行生产。
与简单工厂的区别
在简单工厂模式中,是由工厂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' });
复制代码
抽象工厂模式
定义
抽象工厂模式,又称其他工厂的工厂。可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象
先借助一个图来理解产品族和产品等级
我的咖啡厅一共有三种咖啡,美式,拿铁,卡布奇诺。由于我明智的放弃了前端,选择了星巴克,赚了很多钱,这个时候我决定再开一家咖啡厅,名字叫瑞幸。咖啡的品类和星巴克一样。
产品等级:即产品的继承结构,可以理解为不同家的同一产品。
产品族:指由同一个工厂生产的,位于不同产品等级结构中的一组产品,可以理解为同一家的不同产品,是一组相关或相互依赖的对象。
组成角色
抽象工厂: 提供了创建产品的接口,包含多个创建产品的抽象方法(咖啡工厂)
具体工厂: 实现抽象工厂定义的接口,完成某个具体产品的创建(星巴克工厂和瑞幸工厂)
抽象产品:产品的定义,一般有多少抽象产品,抽象工厂中就包含多少个创建产品的方法(美式咖啡,拿铁咖啡,卡布奇诺咖啡)
具体产品: 抽象产品的实现类(星巴克美式咖啡,瑞幸拿铁咖啡)
类图
咖啡工厂(CafeFactory)
,包含制作三种咖啡的抽象方法,子类星巴克工厂和瑞幸工厂分别生产各自的美式咖啡,拿铁咖啡,卡布奇诺咖啡。
与工厂方法模式的区别
工厂方法模式针对的是同一类或同等级产品,而抽象工厂模式针对的是多种类的产品设计
代码
//抽象类可以继承抽象类
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 {}
复制代码
优点
当系统需要新增一个产品族时,只需要增加新的工厂类即可,无需修改源代码;
缺点
但是如果需要产品族中增加一个新种类的产品时,则所有的工厂类都需要修改