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

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

本文旨在系统性介绍一下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


相关文章
|
8天前
|
前端开发 JavaScript 安全
前端性能调优:HTTP/2与HTTPS在Web加速中的应用
【10月更文挑战第27天】本文介绍了HTTP/2和HTTPS在前端性能调优中的应用。通过多路复用、服务器推送和头部压缩等特性,HTTP/2显著提升了Web性能。同时,HTTPS确保了数据传输的安全性。文章提供了示例代码,展示了如何使用Node.js创建一个HTTP/2服务器。
19 2
|
8天前
|
Rust 前端开发 JavaScript
前端性能革命:WebAssembly在高性能计算中的应用探索
【10月更文挑战第26天】随着Web应用功能的日益复杂,传统JavaScript解释执行模式逐渐成为性能瓶颈。WebAssembly(Wasm)应运而生,作为一种二进制代码格式,支持C/C++、Rust等语言编写的代码在浏览器中高效运行。Wasm不仅提升了应用的执行速度,还具备跨平台兼容性和安全性,显著改善了Web应用的响应速度和用户体验。
24 4
|
8天前
|
前端开发 数据管理 测试技术
前端自动化测试:Jest与Cypress的实战应用与最佳实践
【10月更文挑战第27天】本文介绍了前端自动化测试中Jest和Cypress的实战应用与最佳实践。Jest适合React应用的单元测试和快照测试,Cypress则擅长端到端测试,模拟用户交互。通过结合使用这两种工具,可以有效提升代码质量和开发效率。最佳实践包括单元测试与集成测试结合、快照测试、并行执行、代码覆盖率分析、测试环境管理和测试数据管理。
20 2
|
8天前
|
前端开发 安全 应用服务中间件
前端性能调优:HTTP/2与HTTPS在Web加速中的应用
【10月更文挑战第26天】随着互联网的快速发展,前端性能调优成为开发者的重要任务。本文探讨了HTTP/2与HTTPS在前端性能优化中的应用,介绍了二进制分帧、多路复用和服务器推送等特性,并通过Nginx配置示例展示了如何启用HTTP/2和HTTPS,以提升Web应用的性能和安全性。
15 3
|
8天前
|
前端开发 JavaScript 数据可视化
前端自动化测试:Jest与Cypress的实战应用与最佳实践
【10月更文挑战第26天】前端自动化测试在现代软件开发中至关重要,Jest和Cypress分别是单元测试和端到端测试的流行工具。本文通过解答一系列问题,介绍Jest与Cypress的实战应用与最佳实践,帮助开发者提高测试效率和代码质量。
22 2
|
8天前
|
前端开发 JavaScript API
前端框架新探索:Svelte在构建高性能Web应用中的优势
【10月更文挑战第26天】近年来,前端技术飞速发展,Svelte凭借独特的编译时优化和简洁的API设计,成为构建高性能Web应用的优选。本文介绍Svelte的特点和优势,包括编译而非虚拟DOM、组件化开发、状态管理及响应式更新机制,并通过示例代码展示其使用方法。
24 2
|
9天前
|
前端开发 JavaScript 开发者
“揭秘React Hooks的神秘面纱:如何掌握这些改变游戏规则的超能力以打造无敌前端应用”
【10月更文挑战第25天】React Hooks 自 2018 年推出以来,已成为 React 功能组件的重要组成部分。本文全面解析了 React Hooks 的核心概念,包括 `useState` 和 `useEffect` 的使用方法,并提供了最佳实践,如避免过度使用 Hooks、保持 Hooks 调用顺序一致、使用 `useReducer` 管理复杂状态逻辑、自定义 Hooks 封装复用逻辑等,帮助开发者更高效地使用 Hooks,构建健壮且易于维护的 React 应用。
22 2
|
22天前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
100 2
|
22天前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
34 0
|
23天前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。

热门文章

最新文章

  • 1
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    44
  • 2
    C++一分钟之-C++中的设计模式:单例模式
    51
  • 3
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    36
  • 4
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    59
  • 5
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    54
  • 6
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    39
  • 7
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    49
  • 8
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    102
  • 9
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    74
  • 10
    Go语言设计模式:使用Option模式简化类的初始化
    71