Skip to content

JavaScript 设计模式实践

什么是设计模式

设计模式是解决特定问题的可重用方案,是软件开发中经过验证的最佳实践。

创建型模式

1. 单例模式(Singleton)

确保一个类只有一个实例,并提供全局访问点。

javascript
// ES5 实现
const Singleton = (function() {
  let instance;
  
  function createInstance() {
    return {
      name: "Singleton Instance",
      getData: function() {
        return "Some data";
      }
    };
  }
  
  return {
    getInstance: function() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true

// ES6 实现
class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    Singleton.instance = this;
    return this;
  }
}

const s1 = new Singleton();
const s2 = new Singleton();
console.log(s1 === s2); // true

2. 工厂模式(Factory)

通过工厂函数创建对象,而不直接使用构造函数。

javascript
// 简单工厂
function createUser(type) {
  switch (type) {
    case "admin":
      return {
        role: "admin",
        permissions: ["read", "write", "delete"]
      };
    case "user":
      return {
        role: "user",
        permissions: ["read"]
      };
    default:
      throw new Error("Unknown user type");
  }
}

const admin = createUser("admin");
const user = createUser("user");

// 工厂方法模式
class UserFactory {
  createAdmin() {
    return new Admin();
  }
  
  createUser() {
    return new User();
  }
}

class Admin {
  constructor() {
    this.role = "admin";
  }
}

class User {
  constructor() {
    this.role = "user";
  }
}

3. 建造者模式(Builder)

将复杂对象的构建过程分离,逐步构建对象。

javascript
class PizzaBuilder {
  constructor() {
    this.pizza = {};
  }
  
  setSize(size) {
    this.pizza.size = size;
    return this;
  }
  
  setCrust(crust) {
    this.pizza.crust = crust;
    return this;
  }
  
  addTopping(topping) {
    if (!this.pizza.toppings) {
      this.pizza.toppings = [];
    }
    this.pizza.toppings.push(topping);
    return this;
  }
  
  build() {
    return this.pizza;
  }
}

const pizza = new PizzaBuilder()
  .setSize("large")
  .setCrust("thin")
  .addTopping("cheese")
  .addTopping("pepperoni")
  .build();

console.log(pizza);

结构型模式

4. 适配器模式(Adapter)

将一个类的接口转换成另一个接口,使不兼容的类可以协同工作。

javascript
// 旧接口
class OldCalculator {
  operation(a, b, operation) {
    switch (operation) {
      case "add":
        return a + b;
      case "sub":
        return a - b;
      default:
        return NaN;
    }
  }
}

// 新接口
class NewCalculator {
  add(a, b) {
    return a + b;
  }
  
  sub(a, b) {
    return a - b;
  }
}

// 适配器
class CalculatorAdapter {
  constructor() {
    this.calculator = new NewCalculator();
  }
  
  operation(a, b, operation) {
    switch (operation) {
      case "add":
        return this.calculator.add(a, b);
      case "sub":
        return this.calculator.sub(a, b);
      default:
        return NaN;
    }
  }
}

const adapter = new CalculatorAdapter();
console.log(adapter.operation(5, 3, "add")); // 8

5. 装饰器模式(Decorator)

动态地给对象添加额外的职责。

javascript
// ES6 装饰器(需要 Babel 或 TypeScript)
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()); // 8

6. 代理模式(Proxy)

为其他对象提供代理以控制对这个对象的访问。

javascript
// ES6 Proxy
const target = {
  name: "JavaScript",
  age: 25
};

const proxy = new Proxy(target, {
  get: function(obj, prop) {
    if (prop === "age") {
      return "年龄是私密信息";
    }
    return obj[prop];
  },
  
  set: function(obj, prop, value) {
    if (prop === "age" && value < 0) {
      throw new Error("年龄不能为负数");
    }
    obj[prop] = value;
    return true;
  }
});

console.log(proxy.name); // "JavaScript"
console.log(proxy.age); // "年龄是私密信息"
proxy.age = 26; // 正常设置
// proxy.age = -1; // 抛出错误

行为型模式

7. 观察者模式(Observer)

定义对象间一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知。

javascript
class Subject {
  constructor() {
    this.observers = [];
  }
  
  subscribe(observer) {
    this.observers.push(observer);
  }
  
  unsubscribe(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }
  
  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

class Observer {
  constructor(name) {
    this.name = name;
  }
  
  update(data) {
    console.log(`${this.name} 收到通知: ${data}`);
  }
}

const subject = new Subject();
const observer1 = new Observer("观察者1");
const observer2 = new Observer("观察者2");

subject.subscribe(observer1);
subject.subscribe(observer2);

subject.notify("状态已更新"); 
// 观察者1 收到通知: 状态已更新
// 观察者2 收到通知: 状态已更新

8. 发布-订阅模式(Pub-Sub)

发布-订阅模式是观察者模式的变体,通过事件中心进行通信。

javascript
class EventEmitter {
  constructor() {
    this.events = {};
  }
  
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }
  
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(data));
    }
  }
  
  off(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(cb => cb !== callback);
    }
  }
}

const emitter = new EventEmitter();

emitter.on("user:login", (user) => {
  console.log(`用户 ${user} 已登录`);
});

emitter.on("user:login", (user) => {
  console.log(`发送欢迎邮件给 ${user}`);
});

emitter.emit("user:login", "John");
// 用户 John 已登录
// 发送欢迎邮件给 John

9. 策略模式(Strategy)

定义一系列算法,把它们封装起来,使它们可以互相替换。

javascript
class PaymentStrategy {
  pay(amount) {
    throw new Error("必须实现 pay 方法");
  }
}

class CreditCardStrategy extends PaymentStrategy {
  pay(amount) {
    console.log(`使用信用卡支付 $${amount}`);
  }
}

class PayPalStrategy extends PaymentStrategy {
  pay(amount) {
    console.log(`使用 PayPal 支付 $${amount}`);
  }
}

class PaymentProcessor {
  constructor(strategy) {
    this.strategy = strategy;
  }
  
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  processPayment(amount) {
    this.strategy.pay(amount);
  }
}

const processor = new PaymentProcessor(new CreditCardStrategy());
processor.processPayment(100); // 使用信用卡支付 $100

processor.setStrategy(new PayPalStrategy());
processor.processPayment(200); // 使用 PayPal 支付 $200

10. 命令模式(Command)

将请求封装成对象,从而可以用不同的请求对客户进行参数化。

javascript
class Command {
  execute() {
    throw new Error("必须实现 execute 方法");
  }
  
  undo() {
    throw new Error("必须实现 undo 方法");
  }
}

class Light {
  on() {
    console.log("灯已打开");
  }
  
  off() {
    console.log("灯已关闭");
  }
}

class LightOnCommand extends Command {
  constructor(light) {
    super();
    this.light = light;
  }
  
  execute() {
    this.light.on();
  }
  
  undo() {
    this.light.off();
  }
}

class RemoteControl {
  constructor() {
    this.command = null;
    this.history = [];
  }
  
  setCommand(command) {
    this.command = command;
  }
  
  pressButton() {
    if (this.command) {
      this.command.execute();
      this.history.push(this.command);
    }
  }
  
  undo() {
    if (this.history.length > 0) {
      const command = this.history.pop();
      command.undo();
    }
  }
}

const light = new Light();
const lightOn = new LightOnCommand(light);
const remote = new RemoteControl();

remote.setCommand(lightOn);
remote.pressButton(); // 灯已打开
remote.undo(); // 灯已关闭

总结

设计模式提供了解决常见问题的标准方案:

  • 创建型模式:单例、工厂、建造者
  • 结构型模式:适配器、装饰器、代理
  • 行为型模式:观察者、发布-订阅、策略、命令