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