前端工程中的设计模式应用(上)

简介: 前端工程中的设计模式应用(上)

本文旨在系统性介绍一下23种设计模式,给出通俗易懂的案例、结构图及代码示例,这也是我自身学习理解的过程。或许其中的几种设计模式写的并不是很清晰明了易懂,更详细的可根据提到的参考文献进行深入学习。


什么是设计模式


设计模式这个概念是由一本名为《设计模式:可复用面向对象软件的基础》的书推广而来,这本书在1994年由四个C++工程师编写的。这本书探讨了面向对象的编程的能力和陷阱,并介绍了23种可以用来解决编程问题的模式。这些模式并不是算法或者具体的实现,它们更像是想法、观点和抽象,辅助我们去解决一些特定问题。但是这些模式建立在C++的OOP的基础之上,当使用更现代的编程语言如JavaScript时,模式可能不等效,甚至给代码添加了不必要的限制,但是我们仍然可以把这些模式当做一些编程知识来学习了解。我们学习使用这些模式的目的是实现代码的高内聚和低耦合,提高代码的复用性,让代码更容易的被他人理解并保证代码的可靠性。


23种设计模式简介


 创建型(Creational)(5种)


提供创建对象的机制, 增加已有代码的灵活性和可复用性。

  • 单例模式(Singleton)

保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

  • 原型模式(Prototype)

允许把一个对象作为蓝图创建另一个对象,新对象继承原对象的属性和方法。

  • 生成器模式(Builder)

分“步骤”创建对象。

  • 工厂方法模式(Factory Method)

提供创建对象的接口,对象被创建后可以修改。

  • 抽象工厂模式(Abstract Factory)

允许在不指定具体类的情况下生成一系列相关的对象。

 结构型(Structural)(7种)


介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。

  • 桥接模式(Bridge)

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

  • 外观模式(Facade)

给库、框架以及其他复杂的类集提供简化的接口。

  • 组合模式(Composite)

将对象组合成树形结构来表现整体和部分的层次结构

  • 装饰器模式(Decorator)

通过增加一个修饰对象来包裹原来的对象,从而给原来的对象添加新的行为

  • 适配器模式(Adapter)

允许两个接口不兼容的对象相互交互

  • 代理模式(Proxy)

为另一个对象提供替代或者占位符

  • 享元模式(Flyweight)

运用共享技术来有效地支持大量细粒度对象的复用,以减少创建的对象的数量

 行为型(Behavioral)(11种)


负责对象间的高效沟通和职责委派。

  • 迭代器模式(Iterator)

用于遍历集合的元素

  • 解释器模式(Interpreter )

给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子

  • 观察者模式(Observer)

定义一个订阅机制来通知多个对象它们在观察的对象发生的任何事件

  • 中介者模式(Mediator)

用一个中介对象来封装多个对象之间的复杂交互

  • 访问者模式(Visitor)

针对于对象结构中的元素,定义在不改变该对象的前提下访问其结构中元素的新方法。

  • 状态模式(State)

当一个对象的内部状态发生改变时,会导致其行为的改变,这看起来像是改变了对象

  • 备忘录模式(Memento)

允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。

  • 策略模式(Strategy)

定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

  • 模板方法模式(Template Method)

父类中定义一组操作算法骨架,而将一些实现步骤延迟到子类中,使得子类可以不改变父类的算法结构的同时,重新定义算法中的某些实现步骤。

  • 职责链模式(Chain of Responsibility)

将请求通过处理链传递,链条上的每一个处理程序决定要么处理请求,要么将请求传递给链条上的下一个处理程序。

  • 命令模式(Command)

将请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

设计模式细解


 创建型


创建型模式提供了创建对象的机制, 能够提升已有代码的灵活性和可复用性。


  • 单例模式


单例模式是一种创建型设计模式,它保证一个类只有一个实例, 并提供一个访问该实例的全局节点。


举个现实的例子,比如一个国家只有一个官方政府。不管组成政府的每个人的身份是什么, “某政府” 这一称谓总是鉴别那些掌权者的全局访问节点。


单例模式结构:


代码实现:

class Singleton {
  constructor() {
    this.instance = null; // 单例模式的标识 
  }
  getInstance() {
    if (!this.instance) {
      this.instance = new Singleton(name);
    }
    return this.instance;
  }
}
function clientCode() {
    const s1 = Singleton.getInstance();
    const s2 = Singleton.getInstance();
    if (s1 === s2) {
        console.log('同一个实例');
    } else {
        console.log('不同实例');
    }
}
clientCode(); // 同一个实例
  • 原型模式


原型模式一种创建型设计模式, 它能够复制已有对象,获取原有对象的属性和方法,而又无需使代码依赖它们所属的类。
举个现实的例子,比如细胞的有丝分裂。有丝分裂会产生一对完全相同的细胞。原始细胞就是一个原型, 它在复制体的生成过程中起到了推动作用。
原型模式结构:


代码实现:

class Prototype {
    constructor(name) {
        this.name = name;
    }
    setName(name) {
        this.name = name;
    }
    clone() { 
        return new Prototype(this.name);
    }
}
function clientCode() {
    const p1 = new Prototype('原型模式');
    const p2 = p1.clone();
    if(p1.name === p2.name) {
      console.log('属性复制成功');
    }
    p2.setName('复制的原型模式');
    if(p2.name === '复制的原型模式') {
      console.log('方法复制成功,并不依赖于原先的类');
    }
}
clientCode(); // 属性复制成功   方法复制成功,并不依赖于原先的类
  • 生成器模式


构造器模式是一种创建型设计模式, 它能够分步骤创建复杂对象,可以使用相同的创建代码生成不同类型和形式的对象。
举个现实的例子,比如汽车的制造,A工厂生产轮胎,B工厂生产车门,C工厂生产玻璃,...  最终Z工厂负责组装这些零件,出厂一辆完整的汽车。
生成器模式结构:


简而言之,生成器模式可以概括为两部分,第一部分是具体生成器(ConcreteBuilder)负责new Product,并提供处理Product的各种方法;第二部分主管(Director)负责处理Product的各种方法的调用。


代码实现:

interface Builder {  // 指定创建product对象不同部分的方法
    producePartA(): void;
    producePartB(): void;
    producePartC(): void;
}
// 要创建的产品
class Product {
    public parts: string[] = [];
    public listParts(): void {
        console.log(`Product parts: ${this.parts.join(', ')}\n`);
    }
}
// 具体生成器1
class ConcreteBuilder implements Builder {
    private product: Product1; // 空的产品对象,用于后续的组装
    constructor() {
        this.reset();
    }
    public reset(): void {
        this.product = new Product();
    }
// 所有的生产步骤都是对同一个product实例进行操作
    public producePartA(): void {
        this.product.parts.push('PartA1');
    }
    public producePartB(): void {
        this.product.parts.push('PartB1');
    }
    public producePartC(): void {
        this.product.parts.push('PartC1');
    }
// 获取产品的方法
    public getProduct(): Product {
        const result = this.product;
        this.reset(); // 调用重置方法,做好生产下一个产品的准备,但这并不是必须的
        return result;
    }
}
// 定义创建步骤的执行顺序, 其中生成器负责提供这些步骤的实现。
class Director {
    private builder: Builder;
    public setBuilder(builder: Builder): void {
        this.builder = builder;
    }
    public buildMinimalViableProduct(): void {
        this.builder.producePartA();
    }
    public buildFullFeaturedProduct(): void {
        this.builder.producePartA();
        this.builder.producePartB();
        this.builder.producePartC();
    }
}
// 客户端代码
function clientCode(director: Director) {
    const builder = new ConcreteBuilder(); 
    director.setBuilder(builder); // 将主管类与具体生成器进行关联
    console.log('生成一个基础产品:');
    director.buildMinimalViableProduct();
    builder.getProduct().listParts(); // Product parts: PartA1
    console.log('生成一个完整产品:');
    director.buildFullFeaturedProduct();
    builder.getProduct().listParts(); // Product parts: PartA1, PartB1, PartC1
   // builder类也可以不依赖于Director,可以直接调用其内部的方法
    console.log('生成其他定制产品:');
    builder.producePartA();
    builder.producePartC();
    builder.getProduct().listParts(); // Product parts: PartA1, PartC1
}
const director = new Director();
clientCode(director);
  • 工厂方法模式


工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
工厂方法模式结构:


代码实现:

interface Product { // 声明了所有产品必须实现的操作
    operation(): string;
}
// 创建者类,声明返回产品对象的工厂方法
abstract class Creator {
    public abstract factoryMethod(): Product;
    public someOperation(): string {
        const product = this.factoryMethod(); // 调用工厂方法创建一个产品对象
        return `Creator: 同一个Creator的代码被应用在 ${product.operation()}`;
    }
}
// 具体创建者将重写工厂方法,以改变其所返回的产品类型
class ConcreteCreator1 extends Creator {
    public factoryMethod(): Product {
        return new ConcreteProduct1();
    }
}
class ConcreteCreator2 extends Creator {
    public factoryMethod(): Product {
        return new ConcreteProduct2();
    }
}
// 具体产品需提供产品接口的各种实现
class ConcreteProduct1 implements Product {
    public operation(): string {
        return '{ConcreteProduct1的结果}';
    }
}
class ConcreteProduct2 implements Product {
    public operation(): string {
        return '{ConcreteProduct2的结果}';
    }
}
// 客户端代码
function clientCode(creator: Creator) {
    console.log(creator.someOperation());
}
console.log('App: ConcreteCreator1 启动');
clientCode(new ConcreteCreator1());
//Creator: 同一个Creator的代码被应用在 {ConcreteProduct1的结果}
console.log('App: Launched with the ConcreteCreator2.');
clientCode(new ConcreteCreator2()); 
//Client: I'm not aware of the creator's class, but it still works.
// Creator: 同一个Creator的代码被应用在 {ConcreteProduct2的结果}

简而言之,客户端传入的是具体生成器,然后创建出一个新商品,不同的具体生成器可以产出不同的具体商品。也就是父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。


  • 抽象工厂模式


抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。

抽象工厂模式结构:

代码实现:

// 抽象工厂接口,声明可返回不同抽象产品的方法
interface AbstractFactory {
    createProductA(): AbstractProductA;
    createProductB(): AbstractProductB;
}
// 抽象产品接口
interface AbstractProductA {
    usefulFunctionA(): string;
}
interface AbstractProductB {
    usefulFunctionB(): string;
    anotherUsefulFunctionB(collaborator: AbstractProductA): string;
}
// 具体工厂 可生成属于同一变体的系列产品
class ConcreteFactory1 implements AbstractFactory {
    public createProductA(): AbstractProductA {
        return new ConcreteProductA1();
    }
    public createProductB(): AbstractProductB {
        return new ConcreteProductB1();
    }
}
class ConcreteFactory2 implements AbstractFactory {
    public createProductA(): AbstractProductA {
        return new ConcreteProductA2();
    }
    public createProductB(): AbstractProductB {
        return new ConcreteProductB2();
    }
}
// 具体产品 由相应的具体工厂创建
class ConcreteProductA1 implements AbstractProductA {
    public usefulFunctionA(): string {
        return '这里是A1产品';
    }
}
class ConcreteProductA2 implements AbstractProductA {
    public usefulFunctionA(): string {
        return '这里是A2产品';
    }
}
class ConcreteProductB1 implements AbstractProductB {
    public usefulFunctionB(): string {
        return '这里是B1产品';
    }
    public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
        const result = collaborator.usefulFunctionA();
        return `这里是B1产品合作 (${result})`;
    }
}
class ConcreteProductB2 implements AbstractProductB {
    public usefulFunctionB(): string {
        return '这里是B2产品';
    }
    public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
        const result = collaborator.usefulFunctionA();
        return `这里是B2产品合作 (${result})`;
    }
}
// 客户端代码 仅通过抽象类型AbstractFactory使用工厂和产品
function clientCode(factory: AbstractFactory) {
    const productA = factory.createProductA();
    const productB = factory.createProductB();
    console.log(productB.usefulFunctionB());
    console.log(productB.anotherUsefulFunctionB(productA));
}
clientCode(new ConcreteFactory1()); // 这里是B1产品 // 这里是B1产品合作(A1)
clientCode(new ConcreteFactory2()); // 这里是B2产品 // 这里是B2产品合作(A2)

简而言之,客户端的入参是具体工厂(具体工厂是抽象工厂的实现),具体工厂会生成具体产品。抽象工厂为你提供了一个接口, 可用于创建每个系列产品的对象。只要代码通过该接口创建对象, 那么你就不会生成与应用程序已生成的产品类型不一致的产品。


更多精彩内容,欢迎观看:

前端工程中的设计模式应用(中):https://developer.aliyun.com/article/1396343


相关文章
|
9天前
|
前端开发 安全 开发工具
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
137 90
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
2月前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
137 11
|
2月前
|
设计模式 存储 供应链
前端必须掌握的设计模式——观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,实现了一种订阅机制。它包含两个角色:**观察者**(订阅消息、接收通知并执行操作)和**被观察者**(维护观察者列表、发送通知)。两者通过一对多的关系实现解耦,当被观察者状态改变时,会通知所有订阅的观察者。例如,商店老板作为被观察者,记录客户的需求并在商品到货时通知他们。前端应用中,如DOM事件注册、MutationObserver等也体现了这一模式。
|
14天前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
111 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
10天前
|
人工智能 前端开发 JavaScript
详解智能编码在前端研发的创新应用
接下来,人与智能体的交互将变得更为紧密,比如 N 年以后是否可以逐渐过渡。这个逐渐过渡的过程实际上是温和的,从依赖人类到依赖超大规模算力的转变,可能会取代我们的一些职责。这不仅仅是简单的叠加关系。对于AI和超大规模算力,这是否意味着我们可以大幅度提升软件质量,是否可以缩短研发周期并提高效率,还有创造出更优质的软件并持续发展,这无疑是肯定的。
|
12天前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
35 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
11天前
|
人工智能 前端开发 JavaScript
智能编码在前端研发的创新应用
在前端开发领域,智能编码技术正引领一场变革,通过大模型的强大能力将自然语言需求直接转化为高效、可靠的代码实现。
|
2月前
|
设计模式 存储 缓存
前端必须掌握的设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,旨在将多分支复杂逻辑解耦。每个分支类只关心自身实现,无需处理策略切换。它避免了大量if-else或switch-case代码,符合开闭原则。常见应用场景包括表单验证、风格切换和缓存调度等。通过定义接口和上下文类,策略模式实现了灵活的逻辑分离与扩展。例如,在国际化需求中,可根据语言切换不同的词条包,使代码更加简洁优雅。总结来说,策略模式简化了多条件判断,提升了代码的可维护性和扩展性。
|
2月前
|
设计模式 消息中间件 供应链
前端必须掌握的设计模式——发布订阅模式
发布订阅模式(Publish-Subscribe Pattern)是一种设计模式,类似于观察者模式,但通过引入第三方中介实现发布者和订阅者的解耦。发布者不再直接通知订阅者,而是将消息发送给中介,由中介负责分发给订阅者。这种方式提高了异步支持和安全性,适合复杂、高并发场景,如消息队列和流处理系统。代码实现中,通过定义发布者、订阅者和中介接口,确保消息的正确传递。此模式在前端开发中广泛应用,例如Vue的数据双向绑定。
|
2月前
|
设计模式 前端开发 数据安全/隐私保护
前端必须掌握的设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过引入“替身”对象来间接访问真实对象,从而解耦并提升性能和安全性。例如,知名艺人复出后,经纪人作为代理筛选商单,确保只处理符合团队利益的请求。代码实现中,定义接口`IService`,艺人和经纪人都实现该接口,经纪人在访问时进行过滤和转发。代理模式常用于权限控制、性能优化等场景,如前端中的Tree-shaking和ES6的Proxy构造方法。
前端必须掌握的设计模式——代理模式

热门文章

最新文章

  • 1
    以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
    24
  • 2
    大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
    43
  • 3
    【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
    25
  • 4
    巧用通义灵码,提升前端研发效率
    84
  • 5
    【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    137
  • 6
    详解智能编码在前端研发的创新应用
    92
  • 7
    智能编码在前端研发的创新应用
    75
  • 8
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    35
  • 9
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    111
  • 10
    【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    73