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); // true2. 工厂模式(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")); // 85. 装饰器模式(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()); // 86. 代理模式(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 已登录
// 发送欢迎邮件给 John9. 策略模式(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 支付 $20010. 命令模式(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(); // 灯已关闭总结
设计模式提供了解决常见问题的标准方案:
- 创建型模式:单例、工厂、建造者
- 结构型模式:适配器、装饰器、代理
- 行为型模式:观察者、发布-订阅、策略、命令