"设计模式" (Design Patterns) 是软件工程中的一种解决方案的通用方法。它们提供了一种可重复使用的方法来解决软件开发中的常见问题。设计模式通常是由经验丰富的软件工程师提出的,并在大量项目中进行了实践证明。
一共有 23 种常见的设计模式,它们分为三个类别:创建型模式、结构型模式和行为型模式。
创建型模式:
- 单例模式 (Singleton)
- 工厂模式 (Factory)
- 抽象工厂模式 (Abstract Factory)
- 建造者模式 (Builder)
- 原型模式 (Prototype)
结构型模式:
- 适配器模式 (Adapter)
- 桥接模式 (Bridge)
- 组合模式 (Composite)
- 装饰器模式 (Decorator)
- 外观模式 (Facade)
- 享元模式 (Flyweight)
- 代理模式 (Proxy)
结构型模式:
- 责任链模式 (Chain of Responsibility)
- 命令模式 (Command)
- 解释器模式 (Interpreter)
- 迭代器模式 (Iterator)
- 中介者模式 (Mediator)
- 备忘录模式 (Memento)
- 观察者模式 (Observer)
- 状态模式 (State)
- 策略模式 (Strategy)
- 模板方法模式 (Template Method)
- 访问者模式 (Visitor)
单例模式 (Singleton)
单例模式是一种常用的设计模式,该模式确保一个类只有一个实例,并且提供了一个全局的访问点来访问该实例。
实现单例模式的方法:
// 单例模式的核心代码
class Singleton {
static instance = null;
static getInstance() {
if (Singleton.instance === null) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
// 使用单例模式
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // 输出:true
实际场景代码示例:
一个应用只需要一个数据库连接,那么单例模式就可以确保该连接唯一。
class Database {
static instance = null;
static getInstance() {
if (Database.instance === null) {
Database.instance = new Database();
}
return Database.instance;
}
}
const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2); // 输出:true
工厂模式 (Factory)
工厂模式是一种创建型设计模式,它通过工厂方法创建对象。工厂模式将对象的创建与客户代码隔离,把对象的创建委托给工厂类。
工厂模式的几种常用实现方法:
简单工厂模式:一个工厂类创建所有类型的对象。
class SimpleFactory {
static createProduct(type) {
if (type === 'A') {
return new ProductA();
} else if (type === 'B') {
return new ProductB();
}
return null;
}
}
工厂方法模式:每个子类对应一个工厂类,该工厂类只创建该子类的对象。
class FactoryMethod {
createProduct() {
throw new Error('This method must be overridden');
}
}
class ProductAFactory extends FactoryMethod {
createProduct() {
return new ProductA();
}
}
class ProductBFactory extends FactoryMethod {
createProduct() {
return new ProductB();
}
}
抽象工厂模式:创建多个产品的工厂类。
class AbstractFactory {
createProductA() {
throw new Error('This method must be overridden');
}
createProductB() {
throw new Error('This method must be overridden');
}
}
class ConcreteFactory1 extends AbstractFactory {
createProductA() {
return new ProductA1();
}
createProductB() {
return new ProductB1();
}
}
class ConcreteFactory2 extends AbstractFactory {
createProductA() {
return new ProductA2();
}
createProductB() {
return new ProductB2();
}
}
抽象工厂模式 (Abstract Factory)
抽象工厂模式是一种创建型设计模式,通过抽象工厂类创建相关的多个对象。它是工厂方法模式的升级版本,把对象的创建委托给多个工厂子类。
与工厂方法模式的不同之处在于,工厂方法模式只有一个工厂类,而抽象工厂模式则有多个工厂类。抽象工厂模式允许客户端使用抽象工厂来按需创建对象,而不需要关心对象的具体类型。
代码实现:
// 抽象工厂
class AbstractFactory {
createProductA() {
throw new Error("This method must be overridden");
}
createProductB() {
throw new Error("This method must be overridden");
}
}
// 具体工厂1
class ConcreteFactory1 extends AbstractFactory {
createProductA() {
return new ProductA1();
}
createProductB() {
return new ProductB1();
}
}
// 具体工厂2
class ConcreteFactory2 extends AbstractFactory {
createProductA() {
return new ProductA2();
}
createProductB() {
return new ProductB2();
}
}
// 抽象产品A
class AbstractProductA {
operationA() {
throw new Error("This method must be overridden");
}
}
// 具体产品A1
class ProductA1 extends AbstractProductA {
operationA() {
console.log("ProductA1 operationA");
}
}
// 具体产品A2
class ProductA2 extends AbstractProductA {
operationA() {
console.log("ProductA2 operationA");
}
}
// 抽象产品B
class AbstractProductB {
operationB() {
throw new Error("This method must be overridden");
}
}
// 具体产品B1
class ProductB1 extends AbstractProductB {
operationB() {
console.log("ProductB1 operationB");
}
}
// 具体产品B2
class ProductB2 extends AbstractProduct2 {
operationB() {
console.log("ProductB2 operationB");
}
}
建造者模式是一种创建型设计模式,它是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式通常用于创建复杂的对象,该对象由多个部分组成,每个部分具有不同的属性。
建造者模式包含以下角色:
Builder:抽象建造者,提供创建产品各个子部件的抽象方法。
ConcreteBuilder:具体建造者,实现抽象建造者中的抽象方法,构建和装配各个部件。
Director:指挥者,构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。
Product:产品,是被构建的复杂对象。它包含多个组成部件。
实际场景代码示例:
创建一个复杂的汉堡。
class Burger {
constructor() {
this.ingredients = [];
}
addIngredient(ingredient) {
this.ingredients.push(ingredient);
}
getIngredients() {
return this.ingredients;
}
}
class BurgerBuilder {
constructor() {
this.burger = new Burger();
}
addPatty() {
this.burger.addIngredient('patty');
return this;
}
addCheese() {
this.burger.addIngredient('cheese');
return this;
}
addTomato() {
this.burger.addIngredient('tomato');
return this;
}
addLettuce() {
this.burger.addIngredient('lettuce');
return this;
}
build() {
return this.burger;
}
}
const burgerBuilder = new BurgerBuilder();
const myBurger = burgerBuilder
.addPatty()
.addCheese()
.addTomato()
.addLettuce()
.build();
console.log(myBurger.getIngredients());
// Output: ['patty', 'cheese', 'tomato', 'lettuce']
在上面的示例中,我们使用了建造者模式创建了一个汉堡。我们定义了一个 Burger 类,用于存储汉堡的配料,并定义了一个 BurgerBuilder 类,用于逐步构建汉堡。最后,我们使用 build 方法构建完成的汉堡
适配器模式 (Adapter)
适配器模式是一种结构型设计模式,它允许将一个类的接口转换成另一个接口,以使两个不兼容的类可以一起工作。
在 JavaScript 中,适配器模式常用于将第三方库或现有类与新系统或应用程序集成。
下面是一个示例代码:
class LegacyRectangle {
constructor(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
getArea() {
return (this.x2 - this.x1) * (this.y2 - this.y1);
}
}
class Rectangle {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class LegacyRectangleAdapter {
constructor(legacyRectangle) {
this.legacyRectangle = legacyRectangle;
}
getArea() {
return this.legacyRectangle.getArea();
}
get x() {
return this.legacyRectangle.x1;
}
get y() {
return this.legacyRectangle.y1;
}
get width() {
return this.legacyRectangle.x2 - this.legacyRectangle.x1;
}
get height() {
return this.legacyRectangle.y2 - this.legacyRectangle.y1;
}
}
const legacyRectangle = new LegacyRectangle(1, 1, 3, 3);
const rectangleAdapter = new LegacyRectangleAdapter(legacyRectangle);
console.log(rectangleAdapter.getArea());
// Output: 4
在上面的示例中,我们有一个 LegacyRectangle 类,它表示旧版本的矩形。我们也有一个 Rectangle 类,它表示新版本的矩形。我们使用 LegacyRectangleAdapter 类适配旧版本的矩形,使其兼容新版本的矩形接口。
桥接模式 (Bridge)
桥接模式是一种结构型设计模式,它允许将一个抽象层与它的实现层分离,使得它们可以独立地变化。
在 JavaScript 中,桥接模式可以用于分离一个抽象类的接口与它的实现。这样,当实现需要更改时,不需要更改抽象类,从而简化了维护和测试过程。
下面是一个示例代码:
class AbstractShape {
constructor(renderer) {
this.renderer = renderer;
}
}
class Circle extends AbstractShape {
constructor(renderer, radius) {
super(renderer);
this.radius = radius;
}
toString() {
return this.renderer.renderCircle(this.radius);
}
}
class Square extends AbstractShape {
constructor(renderer, size) {
super(renderer);
this.size = size;
}
toString() {
return this.renderer.renderSquare(this.size);
}
}
class VectorRenderer {
renderCircle(radius) {
return `Drawing a circle of radius ${radius}`;
}
}
class RasterRenderer {
renderCircle(radius) {
return `Drawing pixels for a circle of radius ${radius}`;
}
renderSquare(size) {
return `Drawing pixels for a square of size ${size}`;
}
}
const vector = new VectorRenderer();
const raster = new RasterRenderer();
const circle = new Circle(vector, 5);
console.log(circle.toString());
// Output: Drawing a circle of radius 5
const square = new Square(raster, 10);
console.log(square.toString());
// Output: Drawing pixels for a square of size 10
在上面的代码中,我们有一个抽象类 AbstractShape,它是图形的抽象。我们还有两个图形类 Circle 和 Square,以及两个实现类 VectorRenderer 和 RasterRenderer。每个图形都需要一个渲染器,并且每个图形都可以通过调用 toString 方法得到字符串表示。
在这个示例中,抽象类 AbstractShape 与实现类 VectorRenderer 和 RasterRenderer 之间的关系就是桥接模式的应用。在这种情况下,图形的实现与其渲染的方式是独立的,并且可以独立地进行更改。
桥接模式是一种非常有用的设计模式,可以帮助你解决那些抽象类和实现类之间的复杂关系问题。
组合模式 (Composite)
组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
举个例子,假设我们要创建一个表示文件和文件夹的树形结构。我们可以创建一个 Component 抽象类来定义操作(例如,显示),然后创建两个类:File 类和 Folder 类,它们分别扩展了 Component 类。File 类表示文件,而 Folder 类则表示文件夹,它可以包含一组文件和/或文件夹。
下面是使用 JavaScript 的示例代码:
class Component {
constructor(name) {
this.name = name;
}
display() {
throw new Error('This method must be implemented');
}
}
class File extends Component {
display() {
console.log(`File: ${this.name}`);
}
}
class Folder extends Component {
constructor(name) {
super(name);
this.children = [];
}
add(component) {
this.children.push(component);
}
remove(component) {
this.children = this.children.filter(c => c !== component);
}
display() {
console.log(`Folder: ${this.name}`);
this.children.forEach(c => c.display());
}
}
const folder = new Folder('Root');
folder.add(new File('File1'));
folder.add(new File('File2'));
const subfolder = new Folder('Subfolder');
subfolder.add(new File('File3'));
subfolder.add(new File('File4'));
folder.add(subfolder);
folder.display();
输出:
Folder: Root
File: File1
File: File2
Folder: Subfolder
File: File3
File: File4
``
装饰器模式 (Decorator)
装饰器模式是一种结构型设计模式,它允许你在不改变对象自身的情况下,为对象动态地添加新的行为。它通过创建装饰类来包装原始对象,并在保持类接口不变的同时,给对象添加额外的职责。
举个例子,假设我们要为饮料添加不同的调料,如茶、咖啡等。我们可以创建一个 Beverage 抽象类来定义饮料,然后创建几个具体的饮料类,如 Tea 和 Coffee 类,它们分别继承了 Beverage 类。接下来,我们可以创建几个装饰类,如 Sugar 和 Milk 类,它们也分别继承了 Beverage 类。然后,我们可以创建一个饮料实例,并动态地为其添加调料。
下面是使用 JavaScript 的示例代码:
class Beverage {
constructor(description = 'Unknown Beverage') {
this.description = description;
}
getDescription() {
return this.description;
}
cost() {
throw new Error('This method must be implemented');
}
}
class Tea extends Beverage {
constructor() {
super('Tea');
}
cost() {
return 1;
}
}
class Coffee extends Beverage {
constructor() {
super('Coffee');
}
cost() {
return 2;
}
}
class Sugar extends Beverage {
constructor(beverage) {
super(`${beverage.getDescription()}, Sugar`);
this.beverage = beverage;
}
cost() {
return this.beverage.cost() + 0.5;
}
}
class Milk extends Beverage {
constructor(beverage) {
super(`${beveragegetDescription()}, Milk`);
this.beverage = beverage;
}
cost() {
return this.beverage.cost() + 0.7;
}
}
const tea = new Tea();
console.log(tea.getDescription()); // "Tea"
console.log(tea.cost()); // 1
const coffee = new Coffee();
console.log(coffee.getDescription()); // "Coffee"
console.log(coffee.cost()); // 2
const sugarTea = new Sugar(tea);
console.log(sugarTea.getDescription()); // "Tea, Sugar"
console.log(sugarTea.cost()); // 1.5
const milkCoffee = new Milk(coffee);
console.log(milkCoffee.getDescription()); // "Coffee, Milk"
console.log(milkCoffee.cost()); // 2.7
装饰器模式允许你为对象动态地添加职责,在 JavaScript 中经常用于为对象添加额外的功能,例如为函数添加缓存功能、为 DOM 元素添加事件监听等。
外观模式 (Facade)
外观模式是一种结构型设计模式,它提供了一个简单的接口,隐藏了一个或多个复杂的子系统的复杂性,使得客户端代码可以更容易地使用这些子系统。
举个例子,假设我们有一个计算机类 Computer,它封装了 CPU、内存和硬盘等组件,并且提供了许多操作这些组件的方法。但是,对于客户端来说,直接操作这些组件是比较复杂的,因此我们可以创建一个 ComputerFacade 类来封装这些操作。ComputerFacade 类提供了简单的接口,客户端只需要调用这些接口就可以完成复杂的操作。
下面是使用 JavaScript 的示例代码:
class CPU {
freeze() {
console.log('CPU freeze');
}
jump(position) {
console.log(`CPU jump to position ${position}`);
}
execute() {
console.log('CPU execute');
}
}
class Memory {
load(position, data) {
console.log(`Memory load data ${data} to position ${position}`);
}
}
class HardDrive {
read(position, size) {
console.log(`HardDrive read ${size} bytes from position ${position}`);
return 'data';
}
}
class Computer {
constructor() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
start() {
this.cpu.freeze();
this.memory.load(0, this.hardDrive.read(0, 1024));
this.cpu.jump(0);
this.cpu.execute();
}
}
class ComputerFacade {
constructor() {
this.computer = new Computer();
}
turnOn() {
this.computer.start();
}
}
const computerFacade = new ComputerFacade();
computerFacade.turnOn(); // "CPU freeze", "Memory load data data to position 0", "CPU jump to position 0", "CPU execute"
在这个例子中,我们创建了一个 ComputerFacade 类,它封装了 Computer 类的复杂操作,并提供了一个简单的接口 turnOn()。客户端只需要创建一个 ComputerFacade 实例,然后调用 turnOn() 方法就可以开机了,而不需要知道具体的操作步骤。外观模式使得客户端可以更容易地使用复杂的子系统,并且降低了客户端与子系统的耦合度。
享元模式 (Flyweight)
享元模式是一种结构型设计模式,它旨在通过共享对象来减少内存使用和提高性能。在享元模式中,如果多个对象具有相同的状态,则可以将它们合并为一个共享对象,从而减少内存使用。
举个例子,假设我们要创建一个文本编辑器,其中包含大量的字母和格式化信息。为了减少内存使用,我们可以使用享元模式将相同的字母和格式化信息合并为一个共享对象。
下面是使用 JavaScript 的示例代码:
class Character {
constructor(char, font, size, color) {
this.char = char;
this.font = font;
this.size = size;
this.color = color;
}
render() {
console.log(`Rendering character "${this.char}" in font "${this.font}" with size ${this.size} and color "${this.color}"`);
}
}
class CharacterFactory {
constructor() {
this.characters = {};
}
getCharacter(char, font, size, color) {
const key = `${char}-${font}-${size}-${color}`;
if (!this.characters[key]) {
this.characters[key] = new Character(char, font, size, color);
}
return this.characters[key];
}
}
const characterFactory = new CharacterFactory();
const a1 = characterFactory.getCharacter('a', 'Arial', 12, 'red');
a1.render(); // "Rendering character "a" in font "Arial" with size 12 and color "red""
const a2 = characterFactory.getCharacter('a', 'Arial', 12, 'red');
a2.render(); // "Rendering character "a" in font "Arial" with size 12 and color "red""
console.log(a1 === a2); // true,a1 和 a2 引用的是同一个对象
在这个例子中,我们创建了一个 CharacterFactory 类,它负责创建和管理 Character 对象。如果要创建一个新的 Character 对象,它会先检查是否已经存在具有相同状态的对象,如果已经存在则返回该对象,否则创建一个新的对象并缓存起来。这样就可以避免创建大量相同状态的对象,从而减少内存使用。
代理模式 (Proxy)
代理模式是一种结构型设计模式,它为其他对象提供一个代理,以控制对这个对象的访问。代理对象充当被代理对象的接口,与被代理对象实现相同的接口,从而保持接口的一致性。代理对象可以对调用进行拦截和处理,以实现对被代理对象的访问控制、缓存、延迟加载、远程访问等功能。
举个例子,假设我们有一个图片加载器,它从互联网上下载图片并显示在网页上。由于网络问题或者服务器的限制,图片下载可能非常缓慢或者失败。为了解决这个问题,我们可以使用代理模式,通过代理对象来控制对图片的访问。
下面是使用 JavaScript 的示例代码:
class Image {
constructor(url) {
this.url = url;
}
display() {
console.log(`Displaying image from ${this.url}`);
}
}
class ImageProxy {
constructor(url) {
this.url = url;
}
display() {
if (!this.image) {
this.image = new Image(this.url);
}
this.image.display();
}
}
const image = new ImageProxy('https://example.com/image.jpg');
image.display(); // 首次加载会下载图片并显示,后续加载会从缓存中读取并显示
在这个例子中,我们创建了一个 Image 类,它表示要显示的图片,并实现了 display 方法来显示图片。我们还创建了一个 ImageProxy 类,它充当 Image 对象的代理。ImageProxy 类具有与 Image 类相同的接口,但它只有在需要时才创建 Image 对象,并将请求转发给 Image 对象来处理。这样可以避免对 Image 对象的重复加载,从而提高了性能。
在实际应用中,代理模式还可以用于实现安全代理、远程代理、虚拟代理等功能。例如,安全代理可以对访问进行身份验证和授权;远程代理可以实现对远程对象的访问;虚拟代理可以实现对大对象的延迟加载。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。