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

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

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


相关文章
|
7天前
|
设计模式 前端开发 JavaScript
【JavaScript 技术专栏】JavaScript 设计模式与实战应用
【4月更文挑战第30天】本文探讨JavaScript设计模式在提升开发效率和代码质量中的关键作用。涵盖单例、工厂、观察者、装饰器和策略模式,并通过实例阐述其在全局状态管理、复杂对象创建、实时数据更新、功能扩展和算法切换的应用。理解并运用这些模式能帮助开发者应对复杂项目,提升前端开发能力。
|
7天前
|
缓存 监控 前端开发
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
【4月更文挑战第30天】本文探讨了Flutter应用启动优化策略,包括理解启动过程、资源加载优化、减少初始化工作、界面布局简化、异步初始化、预加载关键数据、性能监控分析以及案例和未来优化方向。通过这些方法,可以缩短启动时间,提升用户体验。使用Flutter DevTools等工具可助于识别和解决性能瓶颈,实现持续优化。
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
|
5天前
|
设计模式 存储 前端开发
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
23 4
|
7天前
|
缓存 移动开发 前端开发
【专栏:HTML与CSS前端技术趋势篇】HTML与CSS在PWA(Progressive Web Apps)中的应用
【4月更文挑战第30天】PWA(Progressive Web Apps)结合现代Web技术,提供接近原生应用的体验。HTML在PWA中构建页面结构和内容,响应式设计、语义化标签、Manifest文件和离线页面的创建都离不开HTML。CSS则用于定制主题样式、实现动画效果、响应式布局和管理字体图标。两者协同工作,保证PWA在不同设备和网络环境下的快速、可靠和一致性体验。随着前端技术进步,HTML与CSS在PWA中的应用将更广泛。
|
7天前
|
前端开发 JavaScript 搜索推荐
【专栏:HTML 与 CSS 前端技术趋势篇】HTML 与 CSS 在 Web 组件化中的应用
【4月更文挑战第30天】本文探讨了HTML和CSS在Web组件化中的应用及其在前端趋势中的重要性。组件化提高了代码复用、维护性和扩展性。HTML提供组件结构,语义化标签增进可读性,支持用户交互;CSS实现样式封装、布局控制和主题定制。案例展示了导航栏、卡片和模态框组件的创建。响应式设计、动态样式、CSS预处理器和Web组件标准等趋势影响HTML/CSS在组件化中的应用。面对兼容性、代码复杂度和性能优化挑战,需采取相应策略。未来,持续发掘HTML和CSS潜力,推动组件化开发创新,提升Web应用体验。
|
7天前
|
前端开发 持续交付 开发工具
【专栏:工具与技巧篇】版本控制与Git在前端开发中的应用
【4月更文挑战第30天】Git是前端开发中的必备工具,它通过分布式版本控制管理代码历史,支持分支、合并、回滚等操作,促进团队协作和冲突解决。在前端项目中,Git用于代码追踪、代码审查、持续集成与部署,提升效率和质量。优化协作包括制定分支策略、编写清晰提交信息、定期合并清理分支及使用Git钩子和自动化工具。掌握Git能有效提升开发效率和代码质量。
|
7天前
|
前端开发 JavaScript 安全
【TypeScript技术专栏】TypeScript在微前端架构中的应用
【4月更文挑战第30天】微前端架构通过拆分应用提升开发效率和降低维护成本,TypeScript作为静态类型语言,以其类型安全、代码智能提示和重构支持强化这一架构。在实践中,TypeScript定义公共接口确保跨微前端通信一致性,用于编写微前端以保证代码质量,且能无缝集成到构建流程中。在微前端架构中,TypeScript是保障正确性和可维护性的有力工具。
|
7天前
|
缓存 监控 前端开发
【Flutter前端技术开发专栏】Flutter应用的性能调优与测试
【4月更文挑战第30天】本文探讨了Flutter应用的性能调优策略和测试方法。性能调优对提升用户体验、降低能耗和增强稳定性至关重要。优化布局(避免复杂嵌套,使用`const`构造函数)、管理内存、优化动画、实现懒加载和按需加载,以及利用Flutter的性能工具(如DevTools)都是有效的调优手段。性能测试包括基准测试、性能分析、压力测试和电池效率测试。文中还以ListView为例,展示了如何实践这些优化技巧。持续的性能调优是提升Flutter应用质量的关键。
【Flutter前端技术开发专栏】Flutter应用的性能调优与测试
|
9月前
|
Web App开发 前端开发 JavaScript
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-fiber解决了什么问题
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-fiber解决了什么问题
98 0
|
9月前
|
前端开发 定位技术
前端学习笔记202305学习笔记第二十三天-地图单线程配置
前端学习笔记202305学习笔记第二十三天-地图单线程配置
66 0
前端学习笔记202305学习笔记第二十三天-地图单线程配置