设计模式是软件开发中解决特定问题的可复用方案。在JavaScript中,合理运用设计模式可以提升代码的可维护性、可扩展性和可复用性。
本文介绍了23种常用的JavaScript设计模式,每种模式都包含实际应用场景、代码实现和可视化示例。
无论你是初学者还是经验丰富的开发者,这些设计模式都将帮助你构建更健壮、更灵活的应用程序。
1. 单例模式 (Singleton)
确保一个类只有一个实例,并提供全局访问点。常用于全局状态管理、日志记录器等场景。
class Logger {
constructor() {
if (!Logger.instance) {
this.logs = [];
Logger.instance = this;
}
return Logger.instance;
}
log(message) {
this.logs.push(message);
console.log(`LOG: ${message}`);
}
}
const logger1 = new Logger();
const logger2 = new Logger();
console.log(logger1 === logger2);
2. 工厂模式 (Factory)
创建对象而不指定具体类。封装实例化逻辑,使代码更灵活、可扩展。
class Car {
constructor(options) {
this.type = options.type || 'sedan';
this.color = options.color || 'white';
}
}
class Truck {
constructor(options) {
this.type = options.type || 'truck';
this.color = options.color || 'blue';
this.capacity = options.capacity || '5t';
}
}
class VehicleFactory {
createVehicle(options) {
switch(options.vehicleType) {
case 'car':
return new Car(options);
case 'truck':
return new Truck(options);
}
}
}
const factory = new VehicleFactory();
const myCar = factory.createVehicle({
vehicleType: 'car',
color: 'red'
});
3. 观察者模式 (Observer)
定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知。
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log(`收到更新: ${data}`);
}
}
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('状态更新!');
4. 策略模式 (Strategy)
定义一系列算法,封装每个算法,并使它们可以互换。让算法的变化独立于使用它的客户端。
class Shipping {
constructor() {
this.strategy = null;
}
setStrategy(strategy) {
this.strategy = strategy;
}
calculate(parcel) {
return this.strategy.calculate(parcel);
}
}
class Fedex {
calculate(parcel) {
return parcel.weight * 3.5;
}
}
class UPS {
calculate(parcel) {
return parcel.weight * 2.8;
}
}
const parcel = { weight: 5 };
const shipping = new Shipping();
shipping.setStrategy(new Fedex());
console.log(`Fedex 费用: ${shipping.calculate(parcel)}`);
shipping.setStrategy(new UPS());
console.log(`UPS 费用: ${shipping.calculate(parcel)}`);
5. 装饰器模式 (Decorator)
动态地给一个对象添加一些额外的职责,相比继承更加灵活。
class Coffee {
cost() {
return 5;
}
}
class MilkDecorator {
constructor(coffee) {
this.coffee = coffee;
}
cost() {
return this.coffee.cost() + 2;
}
}
class SugarDecorator {
constructor(coffee) {
this.coffee = coffee;
}
cost() {
return this.coffee.cost() + 1;
}
}
let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
console.log(`咖啡总价: ${coffee.cost()}元`);
6. 适配器模式 (Adapter)
将一个类的接口转换成客户希望的另一个接口,使原本不兼容的类可以一起工作。
// 适配器模式实现
class OldCalculator {
operations(a, b, operation) {
switch(operation) {
case 'add': return a + b;
case 'sub': return a - b;
}
}
}
class NewCalculator {
add(a, b) { return a + b; }
sub(a, b) { return a - b; }
}
class CalculatorAdapter {
constructor() {
this.calculator = new NewCalculator();
}
operations(a, b, operation) {
switch(operation) {
case 'add': return this.calculator.add(a, b);
case 'sub': return this.calculator.sub(a, b);
}
}
}
// 使用示例
const oldCalc = new OldCalculator();
const adapter = new CalculatorAdapter();
console.log(oldCalc.operations(5, 3, 'add')); // 8
console.log(adapter.operations(5, 3, 'add')); // 8
7. 外观模式 (Facade)
为子系统中的一组接口提供一个统一的高层接口,使子系统更容易使用。
// 外观模式实现
class CPU {
start() { console.log('CPU启动'); }
shutdown() { console.log('CPU关闭'); }
}
class Memory {
load() { console.log('内存加载'); }
clear() { console.log('内存清理'); }
}
class ComputerFacade {
constructor() {
this.cpu = new CPU();
this.memory = new Memory();
}
start() {
this.cpu.start();
this.memory.load();
console.log('电脑启动完成');
}
shutdown() {
this.memory.clear();
this.cpu.shutdown();
console.log('电脑关闭完成');
}
}
// 使用示例
const computer = new ComputerFacade();
computer.start();
computer.shutdown();
8. 代理模式 (Proxy)
为其他对象提供一种代理以控制对这个对象的访问。
// 代理模式实现
class RealImage {
constructor(filename) {
this.filename = filename;
this.loadFromDisk();
}
display() {
console.log(`显示图片: ${this.filename}`);
}
loadFromDisk() {
console.log(`从磁盘加载: ${this.filename}`);
}
}
class ProxyImage {
constructor(filename) {
this.filename = filename;
this.realImage = null;
}
display() {
if (!this.realImage) {
this.realImage = new RealImage(this.filename);
}
this.realImage.display();
}
}
// 使用示例
const image = new ProxyImage('test.jpg');
image.display(); // 第一次会加载
image.display(); // 直接显示
9. 组合模式 (Composite)
将对象组合成树形结构以表示"部分-整体"的层次结构,使客户对单个对象和复合对象的使用具有一致性。
// 组合模式实现
class Employee {
constructor(name, position) {
this.name = name;
this.position = position;
this.subordinates = [];
}
add(employee) {
this.subordinates.push(employee);
}
remove(employee) {
const index = this.subordinates.indexOf(employee);
if (index !== -1) {
this.subordinates.splice(index, 1);
}
}
getSubordinates() {
return this.subordinates;
}
toString() {
return `${this.name} - ${this.position}`;
}
}
// 使用示例
const ceo = new Employee('张三', 'CEO');
const headSales = new Employee('李四', '销售主管');
const headMarketing = new Employee('王五', '市场主管');
ceo.add(headSales);
ceo.add(headMarketing);
const sales1 = new Employee('赵六', '销售员');
const sales2 = new Employee('钱七', '销售员');
headSales.add(sales1);
headSales.add(sales2);
console.log(ceo.toString());
ceo.getSubordinates().forEach(emp => {
console.log(` ${emp.toString()}`);
emp.getSubordinates().forEach(sub => console.log(` ${sub.toString()}`));
});
CEO
- 销售主管
-- 销售员1
-- 销售员2
- 市场主管
10. 享元模式 (Flyweight)
运用共享技术有效地支持大量细粒度的对象。
// 享元模式实现
class Car {
constructor(model, color) {
this.model = model;
this.color = color;
}
}
class CarFactory {
constructor() {
this.cars = {};
}
create(model, color) {
const key = `${model}_${color}`;
if (!this.cars[key]) {
this.cars[key] = new Car(model, color);
}
return this.cars[key];
}
getCount() {
return Object.keys(this.cars).length;
}
}
// 使用示例
const factory = new CarFactory();
const cars = [
factory.create('BMW', 'red'),
factory.create('Audi', 'blue'),
factory.create('BMW', 'red'), // 重复使用
factory.create('BMW', 'blue')
];
console.log(`总创建数: ${cars.length}`);
console.log(`实际对象数: ${factory.getCount()}`);
BMW-red
Audi-blue
BMW-blue
11. 责任链模式 (Chain of Responsibility)
将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求。
// 责任链模式实现
class Handler {
constructor() {
this.nextHandler = null;
}
setNext(handler) {
this.nextHandler = handler;
return handler;
}
handle(request) {
if (this.nextHandler) {
return this.nextHandler.handle(request);
}
return null;
}
}
class Manager extends Handler {
handle(request) {
if (request.amount <= 1000) {
return `经理批准了${request.amount}元的请求`;
}
return super.handle(request);
}
}
class Director extends Handler {
handle(request) {
if (request.amount <= 5000) {
return `总监批准了${request.amount}元的请求`;
}
return super.handle(request);
}
}
class CEO extends Handler {
handle(request) {
if (request.amount <= 10000) {
return `CEO批准了${request.amount}元的请求`;
}
return `需要董事会讨论${request.amount}元的请求`;
}
}
// 使用示例
const manager = new Manager();
const director = new Director();
const ceo = new CEO();
manager.setNext(director).setNext(ceo);
console.log(manager.handle({ amount: 800 })); // 经理
console.log(manager.handle({ amount: 3000 })); // 总监
console.log(manager.handle({ amount: 8000 })); // CEO
console.log(manager.handle({ amount: 20000 }));// 董事会
经理 ≤1000
总监 ≤5000
CEO ≤10000
12. 命令模式 (Command)
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化。
// 命令模式实现
class Light {
on() { console.log('灯打开了'); }
off() { console.log('灯关闭了'); }
}
class Command {
execute() {}
undo() {}
}
class LightOnCommand extends Command {
constructor(light) {
super();
this.light = light;
}
execute() {
this.light.on();
}
undo() {
this.light.off();
}
}
class LightOffCommand extends Command {
constructor(light) {
super();
this.light = light;
}
execute() {
this.light.off();
}
undo() {
this.light.on();
}
}
class RemoteControl {
constructor() {
this.commands = [];
this.history = [];
}
setCommand(command) {
this.commands.push(command);
}
pressButton(index) {
if (index < this.commands.length) {
const cmd = this.commands[index];
cmd.execute();
this.history.push(cmd);
}
}
undo() {
if (this.history.length > 0) {
const cmd = this.history.pop();
cmd.undo();
}
}
}
// 使用示例
const light = new Light();
const remote = new RemoteControl();
remote.setCommand(new LightOnCommand(light));
remote.setCommand(new LightOffCommand(light));
remote.pressButton(0); // 开灯
remote.pressButton(1); // 关灯
remote.undo(); // 撤销最后操作
13. 迭代器模式 (Iterator)
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
// 迭代器模式实现
class Collection {
constructor() {
this.items = [];
}
addItem(item) {
this.items.push(item);
}
getIterator() {
return new Iterator(this);
}
}
class Iterator {
constructor(collection) {
this.collection = collection;
this.index = 0;
}
next() {
return this.collection.items[this.index++];
}
hasNext() {
return this.index < this.collection.items.length;
}
reset() {
this.index = 0;
}
}
// 使用示例
const collection = new Collection();
collection.addItem('Item 1');
collection.addItem('Item 2');
collection.addItem('Item 3');
const iterator = collection.getIterator();
while(iterator.hasNext()) {
console.log(iterator.next());
}
// 重置迭代器
iterator.reset();
console.log('再次遍历:');
while(iterator.hasNext()) {
console.log(iterator.next());
}
14. 备忘录模式 (Memento)
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
// 备忘录模式实现
class Editor {
constructor() {
this.content = '';
}
type(words) {
this.content += words;
}
save() {
return new EditorMemento(this.content);
}
restore(memento) {
this.content = memento.getContent();
}
getContent() {
return this.content;
}
}
class EditorMemento {
constructor(content) {
this.content = content;
}
getContent() {
return this.content;
}
}
// 使用示例
const editor = new Editor();
editor.type('第一段文字。');
const saved = editor.save();
editor.type('第二段文字。');
console.log(editor.getContent()); // 当前内容
editor.restore(saved);
console.log(editor.getContent()); // 恢复到保存点
15. 状态模式 (State)
允许一个对象在其内部状态改变时改变它的行为。
// 状态模式实现
class TrafficLight {
constructor() {
this.states = [new RedLight(), new YellowLight(), new GreenLight()];
this.current = this.states[0];
}
change() {
const totalStates = this.states.length;
let currentIndex = this.states.indexOf(this.current);
this.current = this.states[(currentIndex + 1) % totalStates];
}
sign() {
return this.current.sign();
}
}
class Light {
constructor(light) {
this.light = light;
}
}
class RedLight extends Light {
constructor() {
super('red');
}
sign() {
return 'STOP';
}
}
class YellowLight extends Light {
constructor() {
super('yellow');
}
sign() {
return 'READY';
}
}
class GreenLight extends Light {
constructor() {
super('green');
}
sign() {
return 'GO';
}
}
// 使用示例
const trafficLight = new TrafficLight();
console.log(trafficLight.sign()); // STOP
trafficLight.change();
console.log(trafficLight.sign()); // READY
trafficLight.change();
console.log(trafficLight.sign()); // GO
16. 访问者模式 (Visitor)
表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
// 访问者模式实现
class Employee {
constructor(name, salary) {
this.name = name;
this.salary = salary;
}
accept(visitor) {
visitor.visit(this);
}
}
class Department {
constructor() {
this.employees = [];
}
addEmployee(employee) {
this.employees.push(employee);
}
accept(visitor) {
this.employees.forEach(emp => emp.accept(visitor));
}
}
class SalaryVisitor {
visit(employee) {
employee.salary = Math.floor(employee.salary * 1.1);
console.log(`${employee.name}的新工资: ${employee.salary}`);
}
}
// 使用示例
const dept = new Department();
dept.addEmployee(new Employee('张三', 10000));
dept.addEmployee(new Employee('李四', 15000));
const salaryVisitor = new SalaryVisitor();
dept.accept(salaryVisitor);
17. 桥接模式 (Bridge)
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
// 桥接模式实现
class Printer {
constructor(ink) {
this.ink = ink;
}
}
class EpsonPrinter extends Printer {
print() {
return `Epson打印机使用 ${this.ink.get()} 墨水`;
}
}
class HPPrinter extends Printer {
print() {
return `HP打印机使用 ${this.ink.get()} 墨水`;
}
}
class Ink {
get() {}
}
class BlackInk extends Ink {
get() {
return '黑色';
}
}
class ColorInk extends Ink {
get() {
return '彩色';
}
}
// 使用示例
const blackInk = new BlackInk();
const colorInk = new ColorInk();
const epsonPrinter = new EpsonPrinter(blackInk);
const hpPrinter = new HPPrinter(colorInk);
console.log(epsonPrinter.print());
console.log(hpPrinter.print());
18. 建造者模式 (Builder)
将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。
// 建造者模式实现
class Pizza {
constructor() {
this.toppings = [];
this.crust = '常规';
this.sauce = '番茄';
}
addTopping(topping) {
this.toppings.push(topping);
return this;
}
setCrust(crust) {
this.crust = crust;
return this;
}
setSauce(sauce) {
this.sauce = sauce;
return this;
}
describe() {
return `一个${this.crust}饼底,${this.sauce}酱料的比萨,配料有:${this.toppings.join(', ')}`;
}
}
class PizzaBuilder {
constructor() {
this.pizza = new Pizza();
}
addPepperoni() {
this.pizza.addTopping('意大利辣香肠');
return this;
}
addMushrooms() {
this.pizza.addTopping('蘑菇');
return this;
}
setThinCrust() {
this.pizza.setCrust('薄');
return this;
}
setBBQSauce() {
this.pizza.setSauce('烧烤');
return this;
}
build() {
return this.pizza;
}
}
// 使用示例
const builder = new PizzaBuilder();
const pizza = builder.addPepperoni()
.addMushrooms()
.setThinCrust()
.setBBQSauce()
.build();
console.log(pizza.describe());
19. 原型模式 (Prototype)
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
// 原型模式实现
class Car {
constructor(name, model) {
this.name = name;
this.model = model;
}
clone() {
return new Car(this.name, this.model);
}
}
// 使用示例
const prototypeCar = new Car('Tesla', 'Model S');
const car1 = prototypeCar.clone();
const car2 = prototypeCar.clone();
car1.model = 'Model 3';
console.log(car1); // Tesla Model 3
console.log(car2); // Tesla Model S
20. 中介者模式 (Mediator)
用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用。
// 中介者模式实现
class ChatRoom {
showMessage(user, message) {
const time = new Date().toLocaleTimeString();
console.log(`${time} [${user.getName()}]: ${message}`);
}
}
class User {
constructor(name, chatMediator) {
this.name = name;
this.chatMediator = chatMediator;
}
getName() {
return this.name;
}
send(message) {
this.chatMediator.showMessage(this, message);
}
}
// 使用示例
const chatroom = new ChatRoom();
const john = new User('John', chatroom);
const jane = new User('Jane', chatroom);
john.send('Hi there!');
jane.send('Hey!');
21. 模板方法模式 (Template Method)
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
// 模板方法模式实现
class DataProcessor {
process() {
this.loadData();
this.validateData();
this.analyzeData();
this.reportResults();
}
loadData() {
throw new Error('必须实现loadData方法');
}
validateData() {
console.log('默认数据验证逻辑');
}
analyzeData() {
throw new Error('必须实现analyzeData方法');
}
reportResults() {
console.log('生成报告...');
}
}
class SalesDataProcessor extends DataProcessor {
loadData() {
console.log('加载销售数据...');
}
analyzeData() {
console.log('分析销售趋势...');
}
}
// 使用示例
const processor = new SalesDataProcessor();
processor.process();
22. 解释器模式 (Interpreter)
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
// 解释器模式实现
class Context {
constructor(input) {
this.input = input;
this.output = 0;
}
}
class Expression {
interpret(context) {
if (context.input.startsWith(this.nine())) {
context.output += 9 * this.multiplier();
context.input = context.input.substring(2);
} else if (context.input.startsWith(this.four())) {
context.output += 4 * this.multiplier();
context.input = context.input.substring(2);
} else if (context.input.startsWith(this.five())) {
context.output += 5 * this.multiplier();
context.input = context.input.substring(1);
}
while (context.input.startsWith(this.one())) {
context.output += 1 * this.multiplier();
context.input = context.input.substring(1);
}
}
one() { return ''; }
four() { return ''; }
five() { return ''; }
nine() { return ''; }
multiplier() { return 0; }
}
class ThousandExpression extends Expression {
one() { return 'M'; }
four() { return ' '; }
five() { return ' '; }
nine() { return ' '; }
multiplier() { return 1000; }
}
// 使用示例
const roman = "MCMXXVIII";
const context = new Context(roman);
const tree = [new ThousandExpression()];
tree.forEach(exp => exp.interpret(context));
console.log(`${roman} = ${context.output}`);
23. 空对象模式 (Null Object)
提供一个对象作为缺少给定类型对象的替代品,该对象不做任何动作。
// 空对象模式实现
class User {
constructor(id, name) {
this.id = id;
this.name = name;
}
hasAccess() {
return true;
}
}
class NullUser {
constructor() {
this.id = -1;
this.name = 'Guest';
}
hasAccess() {
return false;
}
}
const users = [
new User(1, 'Alice'),
new User(2, 'Bob')
];
function getUser(id) {
const user = users.find(u => u.id === id);
return user || new NullUser();
}
// 使用示例
console.log(getUser(1).name); // Alice
console.log(getUser(3).name); // Guest
console.log(getUser(3).hasAccess()); // false