前端必须掌握的设计模式——工厂模式

简介: 工厂模式是一种创建型设计模式,通过工厂媒介提供统一接口来创建对象,客户端只需告知创建需求,具体逻辑由工厂处理。工厂模式分为简单工厂、标准工厂和抽象工厂,分别适用于不同场景下的对象创建需求。简单工厂利用静态方法创建对象,标准工厂通过具体工厂类减少耦合,抽象工厂则用于创建一系列相关或依赖对象的家族。

定义

       工厂模式(Factory Pattern)属于创建型设计模式。指的是利用工厂这个媒介,提供统一的接口,从而获得对象。对于客户端而言,只需将创建对象的命令告诉工厂,具体的创建逻辑客户端是不知道的。

       工厂模式可以分为两个角色:

  • 工厂:只负责创建对象的逻辑,不用管其他的逻辑。
  • 产品:调用工厂提供的接口,从而获取对象。

       工厂模式根据不同的场景大致有三种,分别是“简单工厂”、“标准工厂”和“抽象工厂”,以下分别介绍这三种工厂模式变体。 image.gif

image.png

简单工厂

       简单工厂,就是最简单的一种工厂方法,利用一个静态方法,根据传递进来的参数进行判断,从而创建不同的对象。举个例子,我们希望生产一把“AK47”和一把“M4A1”。

image.gif AK47.png

       我们需要定义一个工厂帮我们创建,我们只需要对工厂“下订单”即可。画出UML图如下,先定义一个枪支生产的接口IGun,再定义两个类“Ak47”和“M4a1”分别去实现IGun接口。最后定义一个工厂类GunFactory,在工厂类中定义一个静态方法createGun,通过客户端传递的参数type去映射,是new Ak47()呢还是new M4a1()。

image.gif GunFactory.png

       客户端只需要通过GunFactory类调用静态方法,传递参数完成生产枪的指令,交给GunFactory工厂类去调度, 创建对象最终生产出想要的枪支,代码如下。

// 定义枪械的接口
interface IGun {
  produce(): void; // 生产枪械
}

class Ak47 implements IGun {
  produce() {
    console.log('Ak47 completed!');
  }
}

class M4a1 implements IGun {
  produce() {
    console.log('M4a1 completed!');
  }
}

// 枪械工厂类
class GunFactory {
  static createGun(type: string): IGun {
        switch(type) {
            case 'ak47':
                return new Ak47();
            case 'm4a1':
                return new M4a1();
            default:
                throw new Error('type is not exist');
        }
  }
}

// 客户端 不需要引入具体的产品类 通过工厂类创建具体产品
const gun1 = GunFactory.createGun('ak47');
const gun2 = GunFactory.createGun('m4a1');

gun1.produce(); // Ak47 completed!
gun2.produce(); // M4a1 completed!

标准工厂

       标准工厂的产生,源于简单工厂本身存在一些问题,算是对简单工厂模式的优化,一般也叫做具体工厂。我们细品代码其实可以看出,简单工厂的工厂类职责过多,强耦合;违背开闭原则,如果新增一个AWM类,则需要修改工厂类内部代码,容易对原有逻辑产生影响。

       那么为了解决这个问题,针对工厂类进行解耦,将一个产品对应一个工厂,定义两个工厂“Ak47Factory”和“M4a1Factory”,分别生产“Ak47”和“M4a1”。再定义一个工厂方法,根据传入的对象执行不同产品的生产逻辑。

image.gif T Fectory.png

       客户端通过创建具体的工厂对象,传给factory进行调度,使用创建出来的工厂对象调用对象内部的produce方法,从而生产出想要的枪支,代码如下。

// 定义枪械的接口
interface IGun {
  produce(): void; // 生产枪械
}
class Ak47 implements IGun {
  produce(): void {
    console.log('Ak47 completed!');
  }
}
class M4a1 implements IGun {
  produce(): void {
    console.log('M4a1 completed!');
  }
}
// 定义工厂的接口
interface IFactory {
  createGun(): IGun; // 修改返回类型为 IGun
}
class Ak47Factory implements IFactory {
  createGun(): IGun {
    return new Ak47();
  }
}
class M4a1Factory implements IFactory {
  createGun(): IGun {
    return new M4a1();
  }
}
const factory = (factory: IFactory) => {
  const fac = factory.createGun();
  fac.produce();
};
factory(new Ak47Factory()); // Ak47 completed!
factory(new M4a1Factory()); // M4a1 completed!

对比简单工厂,标准工厂模式如果新增一个AWM,仅仅是添加AWM产品和AWM工厂,并未对之前的产品和工厂产生耦合的逻辑,虽然代码变多了,但属于线性增添是可接受的范围,最重要的是符合开闭原则。

抽象工厂

       有这么一种情况,客户想要创建一系列的产品。举个例子,Ak47、M4a1、Awm这三种类型的枪支都可以做涂装,简单分为炫彩、黄金和英雄,客户想要同时生产黄金Ak47、黄金M4a1、黄金Awm。

image.gif AK47.png

       如果使用标准工厂模式,我们经过排列组合需要创建9个工厂,随着枪支类型和涂装不断增加,我们要新建和维护的工厂呈指数级增长。那么抽象工厂的作用就体现出来了,抽象工厂无需指定类,定义一个抽象工厂AbstractFactory,具体的炫彩、黄金、英雄工厂(这里仅写了炫彩工厂)都继承抽象工厂。

image.gif Pasted Graphic 3.png

       定义一个客户类Client,定义一个方法produceGuns用来执行生产逻辑,这里需要传入对应的具体工厂对象,用该工厂对象调用对应的create方法。

// 抽象产品类 Ak47 M4A1 Awm
abstract class Ak47 {
  abstract produce(): string;
}
abstract class M4a1 {
  abstract produce(): string;
}
abstract class Awm {
  abstract produce(): string;
}
// 具体产品 - 炫彩Ak47
class ColorfulAk47 extends Ak47 {
  produce(): string {
    return 'colorful Ak47';
  }
}
// 具体产品 - 炫彩M4a1
class ColorfulM4a1 extends M4a1 {
  produce(): string {
    return 'colorful M4a1';
  }
}
// 具体产品 - 炫彩Awm
class ColorfulAwm extends M4a1 {
  produce(): string {
    return 'colorful Awm';
  }
}
// 具体产品 - 黄金Ak47
class GoldAk47 extends Ak47 {
  produce(): string {
    return 'gold Ak47';
  }
}
// 具体产品 - 黄金M4a1
class GoldM4a1 extends M4a1 {
  produce(): string {
    return 'gold M4a1';
  }
}
// 具体产品 - 黄金Awm
class GoldAwm extends Awm {
  produce(): string {
    return 'gold Awm';
  }
}
// 具体产品 - 英雄Ak47
class HeroAk47 extends Ak47 {
  produce(): string {
    return 'hero Ak47';
  }
}
// 具体产品 - 英雄M4a1
class HeroM4a1 extends M4a1 {
  produce(): string {
    return 'hero M4a1';
  }
}
// 具体产品 - 英雄Awm
class HeroAwm extends Awm {
  produce(): string {
    return 'hero Awm';
  }
}
// 定义抽象工厂
abstract class AbstractFactory {
  abstract createAk47(): Ak47;
  abstract createM4a1(): M4a1;
  abstract createAwm(): Awm;
}
// 生产炫彩涂装的工厂
class ColorfulFactory extends AbstractFactory{
  createAk47(): Ak47 {
    return new ColorfulAk47();
  }
  createM4a1(): M4a1 {
    return new ColorfulM4a1();
  }
  createAwm(): Awm {
    return new ColorfulAwm();
  }
}
// 客户端类
class Client {
  private factory: AbstractFactory;
  constructor(fac: AbstractFactory) {
    this.factory = fac;
  }
  public produceGuns(): string {
    // 生产对应涂装的一套枪械
    const ak47 = this.factory.createAk47();
    const m4a1 = this.factory.createM4a1();
    const awm = this.factory.createAwm();
    return `${ak47.produce()}-${m4a1.produce()}-${awm.produce()}`;
  }
}
// 客户要生产一套炫彩枪械
const fac = new ColorfulFactory();
const client = new Client(fac);
const info = client.produceGuns();
console.log(info); // colorful Ak47-colorful M4a1-colorful Awm

总结

       总结一下工厂模式,首先是简单工厂是为了避免暴露创建对象具体的细节,但是存在工厂负担过重的问题,所以用标准工厂去解耦,形成一个产品一个工厂的局面;当有一组产品需要生产是引入了抽象工厂,解决群组和系列生产的问题。前端必须掌握的设计模式系列持续更新,如果对您有帮助希望多多点赞哦!

image.gif image.png

相关文章
|
8月前
|
设计模式 前端开发 开发者
探索现代前端开发中的设计模式
在现代前端开发中,设计模式是一种重要的工具,它可以帮助开发者提高代码的可维护性、可扩展性和可重用性。本文将介绍几种常见的设计模式,并探讨它们在前端开发中的应用。
|
8月前
|
设计模式 存储 缓存
精进前端开发:深入探讨前端设计模式
精进前端开发:深入探讨前端设计模式
77 0
|
8月前
|
设计模式 前端开发 算法
前端工程中的设计模式应用(下)
前端工程中的设计模式应用(下)
|
8月前
|
设计模式 前端开发 测试技术
前端设计模式之【工厂模式】
前端设计模式之【工厂模式】
78 0
|
7天前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
|
13天前
|
设计模式 存储 供应链
前端必须掌握的设计模式——观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,实现了一种订阅机制。它包含两个角色:**观察者**(订阅消息、接收通知并执行操作)和**被观察者**(维护观察者列表、发送通知)。两者通过一对多的关系实现解耦,当被观察者状态改变时,会通知所有订阅的观察者。例如,商店老板作为被观察者,记录客户的需求并在商品到货时通知他们。前端应用中,如DOM事件注册、MutationObserver等也体现了这一模式。
|
6天前
|
设计模式 存储 缓存
前端必须掌握的设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,旨在将多分支复杂逻辑解耦。每个分支类只关心自身实现,无需处理策略切换。它避免了大量if-else或switch-case代码,符合开闭原则。常见应用场景包括表单验证、风格切换和缓存调度等。通过定义接口和上下文类,策略模式实现了灵活的逻辑分离与扩展。例如,在国际化需求中,可根据语言切换不同的词条包,使代码更加简洁优雅。总结来说,策略模式简化了多条件判断,提升了代码的可维护性和扩展性。
|
11天前
|
设计模式 消息中间件 供应链
前端必须掌握的设计模式——发布订阅模式
发布订阅模式(Publish-Subscribe Pattern)是一种设计模式,类似于观察者模式,但通过引入第三方中介实现发布者和订阅者的解耦。发布者不再直接通知订阅者,而是将消息发送给中介,由中介负责分发给订阅者。这种方式提高了异步支持和安全性,适合复杂、高并发场景,如消息队列和流处理系统。代码实现中,通过定义发布者、订阅者和中介接口,确保消息的正确传递。此模式在前端开发中广泛应用,例如Vue的数据双向绑定。
|
24天前
|
设计模式 前端开发 JavaScript
前端必须掌握的设计模式——装饰器模式
装饰器模式是一种结构型设计模式,通过创建新类来包装原始对象,实现在不修改原有结构的前提下扩展新行为。其核心在于“组合”思想,使新功能可“即插即拔”。该模式具有解耦性、灵活性和动态性等特点,广泛应用于类的面向对象编程语言中,如JavaScript的注解和TypeScript的写法。示例中,通过装饰器模式为游戏角色动态添加装备,展示了其强大的扩展性和灵活性。
|
17天前
|
设计模式 前端开发 数据安全/隐私保护
前端必须掌握的设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过引入“替身”对象来间接访问真实对象,从而解耦并提升性能和安全性。例如,知名艺人复出后,经纪人作为代理筛选商单,确保只处理符合团队利益的请求。代码实现中,定义接口`IService`,艺人和经纪人都实现该接口,经纪人在访问时进行过滤和转发。代理模式常用于权限控制、性能优化等场景,如前端中的Tree-shaking和ES6的Proxy构造方法。
前端必须掌握的设计模式——代理模式