JS中常见的设计模式

159 阅读6分钟

什么是设计模式

做菜要有菜谱,游戏要有攻略,每个领域都存在一些能够让我们又好又快地达成目标的“套路”。在程序世界,编程的“套路”就是设计模式

设计模式是一套被反复使用的、经过验证的、用于解决软件设计中常见问题的通用解决方案。它们并不是具体的代码,而是对代码组织和架构的最佳实践的描述,旨在帮助开发者以一种更高效、更优雅的方式构建软件。

为什么要使用设计模式

  1. 设计模式提供了一种标准化的解决方案,使得开发者可以复用这些解决方案来解决类似的问题。
  2. 通过使用设计模式,开发者可以减少开发时间并避免陷入常见的陷阱或错误。
  3. 设计模式有助于构建结构清晰、易于理解和维护的代码。

开发者对设计模式的掌握程度,一定程度上反映着他用健壮的代码去解决具体的问题的能力

设计模式分类

设计模式基于它们解决问题的核心关注点和目的分为三类:创建型模式、结构型模式和行为型模式。分别专注于对象的创建过程、对象之间的组合与关系以及对象之间的交互与责任分配。

创建型模式

创建型模式用于解决与对象创建情况相关的常见问题。

单例模式

单例模式确保一个类只有一个实例,并提供一个全局访问点。实现的方法就是先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

class Singleton {
    constructor() {
        if (Singleton.instance) {
            return Singleton.instance;
        }
        Singleton.instance = this;
        this.data = {};
    }

    getData() {
        return this.data;
    }

    setData(key, value) {
        this.data[key] = value;
    }
}

const instance1 = new Singleton();
instance1.setData('name', '这是单例模式');

const instance2 = new Singleton();
console.log(instance2.getData()); // { name: '这是单例模式' }
console.log(instance1 === instance2); // true 即同一个实例

工厂模式

工厂模式定义了一个创建对象的接口,允许子类决定实例化哪一个类。

class Animal {
  speak() {
    throw new Error('方法必须被实现');
  }
}

class Dog extends Animal {
  speak() {
    return '汪汪!';
  }
}

class Cat extends Animal {
  speak() {
    return '喵喵!';
  }
}

// 实现工厂方法
class AnimalFactory {
  createAnimal() {
      return new Error("未实现")
  }
}

class ConcreteFactoryDog extends AnimalFactory {
   createAnimal(){
       return new Dog();
   }
}

class ConcreteFactoryCat extends AnimalFactory {
   createAnimal(){
       return new Cat();
   }
}

const dog = new ConcreteFactoryDog()
console.log(dog.createAnimal().speak()); // 汪汪!
const cat = new ConcreteFactoryCat()
console.log(cat.createAnimal().speak()); // 喵喵!

抽象工厂模式

抽象工厂和工厂模式的区别在于,工厂模式只生产一大类对象,而抽象工厂可以生产好几大类对象,就是所谓1n的区别。抽象工厂模式存在灵活性差的问题,变更时会违反开比原则。

// 创建一组主题对象类型的抽象类
class AnimalFood {
  provide() {
    throw new Error('未实现');
  }
}

class AnimalToy {
  provide() {
    throw new Error('未实现');
  }
}

// 创建一组具体代表家族的对象
class HighQualityDogFood extends AnimalFood {
  provide() {
    return '高品质狗粮';
  }
}

class HighQualityDogToy extends AnimalToy {
  provide() {
    return '高品质狗玩偶';
  }
}

class CheapCatFood extends AnimalFood {
  provide() {
    return '便宜猫粮';
  }
}

class CheapCatToy extends AnimalToy {
  provide() {
    return '便宜猫玩偶';
  }
}

// 创建一个抽象工厂
class AnimalProductsAbstractFactory {
  createFood() {
    throw new Error('未实现');
  }

  createToy() {
    throw new Error('未实现');
  }
}

// 创建具体工厂类
class HighQualityAnimalProductsFactory extends AnimalProductsAbstractFactory {
  createFood() {
    return new HighQualityDogFood();
  }

  createToy() {
    return new HighQualityDogToy();
  }
}

class CheapAnimalProductsFactory extends AnimalProductsAbstractFactory {
  createFood() {
    return new CheapCatFood();
  }

  createToy() {
    return new CheapCatToy();
  }
}

// 使用具体工厂类来创建相关的对象
const highQualityAnimalProductsFactory = new HighQualityAnimalProductsFactory();
console.log(highQualityAnimalProductsFactory.createFood().provide()); // 高品质狗粮
console.log(highQualityAnimalProductsFactory.createToy().provide()); // 高品质狗玩偶

const cheapAnimalProductsFactory = new CheapAnimalProductsFactory();
console.log(cheapAnimalProductsFactory.createFood().provide()); // 便宜猫粮
console.log(cheapAnimalProductsFactory.createToy().provide()); // 便宜猫玩偶

原型模式

通过复制现有的对象来创建新对象,而不是通过实例化类。当对象的创建过程复杂或开销较大时用的比较多。

// 原型接口
class Prototype {
    clone() {
        throw new Error("未实现");
    }
}

// 具体原型
class ConcretePrototype extends Prototype {
    constructor(value) {
        super();
        this.value = value;
    }

    clone() {
        return new ConcretePrototype(this.value);
    }
}

const original = new ConcretePrototype('原始值');
const clone = original.clone();

console.log(original.value); // 原始值
console.log(clone.value); // 原始值

clone.value = '克隆值';

console.log(original.value); // 原始值
console.log(clone.value); // 克隆值

结构型模式

关注对象之间的组合,通过继承或接口的方式来实现不同对象之间的协作。

适配器模式

适配器通过将目标接口与现有接口适配,使得客户端可以使用不兼容的接口。

// 目标接口
class Target {
  request() {
    console.log('Target: 请求已被调用');
  }
}

// 需要适配的类
class Adaptee {
  specificRequest() {
    console.log('Adaptee 方法已被访问');
  }
}

// 适配器类,将 Adaptee 转换为 Target
class Adapter extends Target {
  constructor(adaptee) {
    super();
    this.adaptee = adaptee;
  }

  request() {
    this.adaptee.specificRequest();
  }
}

// 使用适配器将客户端与 Adaptee 解耦
const client = new Adapter(new Adaptee());
client.request(); // Adaptee 方法已被访问

装饰者模式

允许在不改变对象自身的情况下,动态地为其添加新功能。

// 抽象组件类
class Component {
  operation() {
    throw new Error("未实现");
  }
}

// 具体组件类
class ConcreteComponent extends Component {
  operation() {
    return 'ConcreteComponent:具体操作'
  }
}

// 抽象装饰器类
class Decorator extends Component {
  constructor(component) {
    super();
    this.component = component;
  }

  operation() {
    this.component.operation();
  }
}

// 具体装饰器类
class ConcreteDecoratorA extends Decorator {
  operation() { 
    super.operation();
    return 'ConcreteDecoratorA:添加操作'
  }
}

class ConcreteDecoratorB extends Decorator {
  operation() { 
    super.operation();
    return 'ConcreteDecoratorB:添加操作'
  }
}

// 使用装饰器组合对象
const component = new ConcreteComponent();
console.log(component.operation()); // ConcreteComponent:具体操作
const decoratorA = new ConcreteDecoratorA(component);
console.log(decoratorA.operation()); // ConcreteDecoratorA:添加操作
const decoratorB = new ConcreteDecoratorB(decoratorA);
console.log(decoratorB.operation()); // ConcreteDecoratorB:添加操作

代理模式

通过为一个对象提供一个代理来控制对该对象的访问。

// 主题接口
class Subject {
    request() {
        throw new Error("未实现");
    }
}

// 真实主题
class RealSubject extends Subject {
    request() {
        return 'RealSubject: 发送请求';
    }
}

// 代理类
class Proxy extends Subject {
    constructor(realSubject) {
        super();
        this.realSubject = realSubject;
    }

    request() {
        // 可以添加额外的功能
        console.log('Proxy: 在真实请求前记录');
        return this.realSubject.request();
    }
}

const realSubject = new RealSubject();
const proxy = new Proxy(realSubject);
console.log(proxy.request()); // Proxy: 在真实请求前记录
                              // RealSubject: 发送请求

行为型模式

关注对象之间的责任分配和行为互动,描述类或对象之间如何进行通信。

观察者模式

用于建立一种一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。

// 被观察者
class Subject {
    constructor() {
        this.observers = [];
    }

    addObserver(observer) {
        this.observers.push(observer);
    }

    removeObserver(observer) {
        this.observers = this.observers.filter(obs => obs !== observer);
    }

    notifyObservers(data) {
        this.observers.forEach(observer => observer.update(data));
    }
}

// 观察者接口
class Observer {
    update(data) {
        throw new Error("未实现");
    }
}

// 具体观察者
class ConcreteObserverA extends Observer {
    update(data) {
        console.log(`ConcreteObserverA: 对${data}做出响应`);
    }
}

class ConcreteObserverB extends Observer {
    update(data) {
        console.log(`ConcreteObserverB: 对${data}做出响应`);
    }
}

const subject = new Subject();

const observerA = new ConcreteObserverA();
const observerB = new ConcreteObserverB();

subject.addObserver(observerA);
subject.addObserver(observerB);

subject.notifyObservers('事件1'); // ConcreteObserverA: 对事件1做出响应
                                  // ConcreteObserverB: 对事件1做出响应
subject.removeObserver(observerA);
subject.notifyObservers('事件2'); // 输出: ConcreteObserverB: 对事件2做出响应

参考

总结

设计模式帮助开发者避免重复造轮子,构建更健壮、可扩展和易于维护的软件系统。如今同样已经成为前端面试中无法回避、同时具有较高候选人区分度的一个核心考点。学好设计模式对面试、工作都有很大的帮助,一起加油!

最后

以上就是JS中常见的设计模式介绍,如果需要补充,欢迎评论!

码字不易,感谢三连!

已将学习代码上传至 github,欢迎大家学习指正!

技术小白记录学习过程,有错误或不解的地方还请评论区留言,如果这篇文章对你有所帮助请 “点赞 收藏+关注” ,感谢支持!!