JavaScript设计模式

什么是设计模式

设计模式是解决软件设计中常见问题的可复用解决方案。它们是经过验证的最佳实践,可以帮助开发者:

  • 提高代码的可维护性和可复用性
  • 使代码更具可读性和可理解性
  • 促进团队成员之间的有效沟通
  • 减少重复工作,提高开发效率

JavaScript中常用的设计模式

1. 单例模式(Singleton Pattern)

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

javascript
// 单例模式的实现
const Singleton = (function() {
  let instance;

  function createInstance() {
    const object = new Object({ name: 'Singleton' });
    return object;
  }

  return {
    getInstance: function() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

// 使用单例模式
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

2. 工厂模式(Factory Pattern)

工厂模式提供一个创建对象的接口,让子类决定实例化哪一个类。

javascript
// 工厂模式的实现
function AnimalFactory() {
  this.createAnimal = function(type) {
    let animal;
    
    if (type === 'dog') {
      animal = new Dog();
    } else if (type === 'cat') {
      animal = new Cat();
    } else if (type === 'bird') {
      animal = new Bird();
    }
    
    animal.speak = function() {
      return `${this.name} says ${this.sound}!`;
    };
    
    return animal;
  };
}

function Dog() {
  this.name = 'Dog';
  this.sound = 'Woof';
}

function Cat() {
  this.name = 'Cat';
  this.sound = 'Meow';
}

function Bird() {
  this.name = 'Bird';
  this.sound = 'Tweet';
}

// 使用工厂模式
const factory = new AnimalFactory();
const dog = factory.createAnimal('dog');
const cat = factory.createAnimal('cat');

console.log(dog.speak()); // Dog says Woof!
console.log(cat.speak()); // Cat says Meow!

3. 观察者模式(Observer Pattern)

观察者模式定义了对象之间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动收到通知并更新。

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} received data: ${data}`);
  }
}

// 使用观察者模式
const subject = new Subject();
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');

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

subject.notify('Hello World!');
// Observer 1 received data: Hello World!
// Observer 2 received data: Hello World!

subject.unsubscribe(observer1);

subject.notify('Goodbye!');
// Observer 2 received data: Goodbye!

4. 装饰器模式(Decorator Pattern)

装饰器模式允许向一个现有对象添加新的功能,同时不改变其结构。

javascript
// 装饰器模式的实现
class Coffee {
  cost() {
    return 5;
  }
  
  description() {
    return 'Basic Coffee';
  }
}

class MilkDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }
  
  cost() {
    return this.coffee.cost() + 2;
  }
  
  description() {
    return this.coffee.description() + ', Milk';
  }
}

class SugarDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }
  
  cost() {
    return this.coffee.cost() + 1;
  }
  
  description() {
    return this.coffee.description() + ', Sugar';
  }
}

// 使用装饰器模式
let coffee = new Coffee();
console.log(`${coffee.description()} costs ${coffee.cost()}`); // Basic Coffee costs $5

coffee = new MilkDecorator(coffee);
console.log(`${coffee.description()} costs ${coffee.cost()}`); // Basic Coffee, Milk costs $7

coffee = new SugarDecorator(coffee);
console.log(`${coffee.description()} costs ${coffee.cost()}`); // Basic Coffee, Milk, Sugar costs $8

5. 策略模式(Strategy Pattern)

策略模式定义了一系列算法,把它们一个个封装起来,并使它们可以互相替换。

javascript
// 策略模式的实现
class ShoppingCart {
  constructor(discountStrategy) {
    this.discountStrategy = discountStrategy;
    this.items = [];
  }
  
  addItem(item) {
    this.items.push(item);
  }
  
  calculateTotal() {
    const subtotal = this.items.reduce((total, item) => total + item.price, 0);
    return this.discountStrategy.calculateDiscount(subtotal);
  }
  
  setDiscountStrategy(strategy) {
    this.discountStrategy = strategy;
  }
}

class NoDiscount {
  calculateDiscount(subtotal) {
    return subtotal;
  }
}

class PercentageDiscount {
  constructor(percentage) {
    this.percentage = percentage;
  }
  
  calculateDiscount(subtotal) {
    return subtotal - (subtotal * this.percentage / 100);
  }
}

class FixedDiscount {
  constructor(amount) {
    this.amount = amount;
  }
  
  calculateDiscount(subtotal) {
    return subtotal - Math.min(this.amount, subtotal);
  }
}

// 使用策略模式
const cart = new ShoppingCart(new NoDiscount());
cart.addItem({ name: 'Item 1', price: 100 });
cart.addItem({ name: 'Item 2', price: 200 });

console.log(`Total without discount: ${cart.calculateTotal()}`); // Total without discount: $300

cart.setDiscountStrategy(new PercentageDiscount(10));
console.log(`Total with 10% discount: ${cart.calculateTotal()}`); // Total with 10% discount: $270

cart.setDiscountStrategy(new FixedDiscount(50));
console.log(`Total with $50 discount: ${cart.calculateTotal()}`); // Total with $50 discount: $250

6. 代理模式(Proxy Pattern)

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

javascript
// 代理模式的实现
class RealImage {
  constructor(filename) {
    this.filename = filename;
    this.loadFromDisk();
  }
  
  loadFromDisk() {
    console.log(`Loading image: ${this.filename}`);
  }
  
  display() {
    console.log(`Displaying image: ${this.filename}`);
  }
}

class ImageProxy {
  constructor(filename) {
    this.filename = filename;
    this.realImage = null;
  }
  
  display() {
    if (this.realImage === null) {
      this.realImage = new RealImage(this.filename);
    }
    this.realImage.display();
  }
}

// 使用代理模式
const image = new ImageProxy('test.jpg');

// 图像不会被加载,直到调用display()
console.log('Image proxy created');

// 现在图像会被加载并显示
image.display();

// 图像已经加载过,只会显示
image.display();

7. 模块模式(Module Pattern)

模块模式用于创建具有私有和公共成员的模块。

javascript
// 模块模式的实现
const Calculator = (function() {
  // 私有成员
  let result = 0;
  
  function validateNumber(num) {
    return typeof num === 'number' && !isNaN(num);
  }
  
  // 公共成员
  return {
    add: function(a, b) {
      if (validateNumber(a) && validateNumber(b)) {
        result = a + b;
        return result;
      }
      return 'Invalid numbers';
    },
    
    subtract: function(a, b) {
      if (validateNumber(a) && validateNumber(b)) {
        result = a - b;
        return result;
      }
      return 'Invalid numbers';
    },
    
    multiply: function(a, b) {
      if (validateNumber(a) && validateNumber(b)) {
        result = a * b;
        return result;
      }
      return 'Invalid numbers';
    },
    
    divide: function(a, b) {
      if (validateNumber(a) && validateNumber(b)) {
        if (b === 0) {
          return 'Cannot divide by zero';
        }
        result = a / b;
        return result;
      }
      return 'Invalid numbers';
    },
    
    getResult: function() {
      return result;
    }
  };
})();

// 使用模块模式
console.log(Calculator.add(5, 3)); // 8
console.log(Calculator.subtract(5, 3)); // 2
console.log(Calculator.multiply(5, 3)); // 15
console.log(Calculator.divide(6, 3)); // 2
console.log(Calculator.getResult()); // 2

8. 迭代器模式(Iterator Pattern)

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部表示。

javascript
// 迭代器模式的实现
class Iterator {
  constructor(collection) {
    this.collection = collection;
    this.index = 0;
  }
  
  next() {
    return this.collection[this.index++];
  }
  
  hasNext() {
    return this.index < this.collection.length;
  }
}

class NumberCollection {
  constructor(numbers) {
    this.numbers = numbers;
  }
  
  createIterator() {
    return new Iterator(this.numbers);
  }
}

// 使用迭代器模式
const numbers = new NumberCollection([1, 2, 3, 4, 5]);
const iterator = numbers.createIterator();

while (iterator.hasNext()) {
  console.log(iterator.next()); // 1, 2, 3, 4, 5
}

设计模式最佳实践

  1. 理解问题:在应用设计模式之前,确保你充分理解了问题
  2. 选择合适的模式:根据问题选择最适合的设计模式
  3. 不要过度使用:设计模式不是银弹,不要为了使用模式而使用模式
  4. 遵循原则:设计模式应该遵循SOLID原则
  5. 保持简单:尽量使用简单的解决方案,只有在必要时才使用复杂的设计模式

总结

设计模式是软件开发中的重要概念,它们提供了经过验证的解决方案来解决常见的设计问题。在JavaScript中,我们可以使用各种设计模式来提高代码的可维护性、可复用性和可读性。

选择合适的设计模式取决于具体的问题和需求。在实际开发中,应该根据项目的规模、复杂度和团队的熟悉程度来选择合适的设计模式。