内功大法:六大设计原则和23 种设计模式

185 阅读8分钟

六大设计原则

1. 单一职责原则(SRP)

Single Responsibility Principle

一个类应该只有一个引起它变化的原因。

// 不推荐:用户即可获取用户信息和发送邮件
class UserService {
  getName() { /*  */ }
  sendEmail(msg: string) { /*  */ }
}

// 推荐:将职责分离
class UserService {
  getName() { /*  */ } 
}
class EmailService {
  sendEmail(msg: string) { /*  */ }
}

2. 开放封闭原则(OCP)

Open/Closed Principle

对扩展开放,对修改封闭。

// 不遵循 OCP 的设计
class AreaCalculator {
  calculate(shape) {
    if (shape.type === 'circle') {
      console.log(Math.PI * shape.radius ** 2);
    } else if (shape.type === 'rectangle') {
      console.log(shape.width * shape.height);
    }
    // 每增加一种新图形,就要修改这个方法!
  }
} 


// 图形接口
interface Shape {
  calculateArea(): number;
}

// 圆形
class Circle implements Shape {
  constructor(public radius: number) {}

  calculateArea(): number {
    return Math.PI * this.radius ** 2;
  }
}

// 矩形
class Rectangle implements Shape {
  constructor(public width: number, public height: number) {}

  calculateArea(): number {
    return this.width * this.height;
  }
}

//计算打印
class AreaCalculator {
  calculate(shapes: Shape) {
      console.log(shape.calculateArea())
  }
}

let circle = new Circle(10) 
let rectangle = new Rectangle(10,20)
new AreaCalculator().calculate(circle)
new AreaCalculator().calculate(rectangle)

3. 里氏替换原则(LSP)

Liskov Substitution Principle

子类必须能够替换父类,并保持行为一致。

// 不推荐:子类行为违背父类预期
class Bird {
  fly() {
    console.log('Flying');
  }
}
class Penguin extends Bird {
  fly() {
    throw new Error("Penguins can't fly");
  }
}

// 推荐:重新组织类结构
class Animal {}

class FlyingBird extends Animal {
  fly() {
    console.log('Flying');
  }
}

class PenguinV2 extends Animal {
  swim() {
    console.log('Swimming');
  }
}

4. 接口隔离原则(ISP)

Interface Segregation Principle

不要强迫依赖它不需要的接口。和单一职责类似

// 臃肿的接口
interface Worker {
    void work();
    void eat();
    void sleep();
}

// 机器人不需要“eat”和“sleep”
class Robot implements Worker {
    @Override
    public void work() { System.out.println("机器人工作"); }

    @Override
    public void eat() { /* 机器人不需要吃 */ }

    @Override
    public void sleep() { /* 机器人不需要睡觉 */ }
} 


// 推荐 
// 拆分成多个小接口
interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

interface Sleepable {
    void sleep();
}

// 机器人只实现Workable
class Robot implements Workable {
    @Override
    public void work() { System.out.println("机器人工作"); }
} 

5. 依赖倒置原则(DIP)

Dependency Inversion Principle

高层模块不应该依赖低层模块实例,而应该依赖抽象。

// 不推荐:依赖具体的水果存储方式
class LocalStorage {
  save(fruit: any) {
    console.log('Saved to local storage');
  }
}

class FruitApp {
  storage = new LocalStorage();
  saveFruit(fruit: any) {
    this.storage.save(fruit);
  }
}

// 推荐:依赖抽象接口
interface Storage {
  save(fruit: any): void;
}

class LocalStorageV2 implements Storage {
  save(fruit: any) {
    console.log('Saved to local storage');
  }
}

class CloudStorage implements Storage {
  save(fruit: any) {
    console.log('Saved to cloud');
  }
}

class FruitAppV2 {
  constructor(private storage: Storage) {}
  saveFruit(fruit: any) {
    this.storage.save(fruit);
  }
}

6. 迪米特法则 (LOD)

Law of Demeter

最少知道原则 一个对象应该对其他对象有尽可能少的了解,只与“直接的朋友”通信。 不要对象调用对象再调用属性。

// 不推荐:多个对象链式调用
// Department 类
class Department {
    private company: Company;

    constructor(company: Company) {
        this.company = company;
    }

    public getCompany(): Company {
        return this.company;
    }
}

// Company 类
class Company {
    private name: string;

    constructor(name: string) {
        this.name = name;
    }

    public getName(): string {
        return this.name;
    }
}

// Person 类
class Person {
    private department: Department;

    constructor(department: Department) {
        this.department = department;
    }

    public getDepartment(): Department {
        return this.department;
    }
}

// Client 直接进行链式调用,依赖了 Person -> Department -> Company 的内部结构
const getCompanyName = () => {
    const company = new Company("Acme Corp");
    const department = new Department(company);
    const person = new Person(department);

    // 链式调用,如果中间任何一环变化,这里就会出错
    const companyName = person.getDepartment().getCompany().getName();
    console.log(companyName); // 输出: Acme Corp
};
getCompanyName();


// 推荐

// Person 类
class Person {
    private department: Department;

    constructor(department: Department) {
        this.department = department;
    }

    // 提供一个高层方法,封装内部细节,Client 不需要知道 Department 和 Company
    public getCompanyName(): string {
        return this.department.getCompany().getName();
    }
}

// Client 只与 Person 打交道,不关心内部的 Department 和 Company
const getCompanyName = () => {
    const company = new Company("Acme Corp");
    const department = new Department(company);
    const person = new Person(department);

    // 只调用 Person 提供的方法,不涉及内部结构
    const companyName = person.getCompanyName();
    console.log(companyName); // 输出: Acme Corp
};
getCompanyName();

23 种设计模式

设计模式分为三大类:创建型、结构型、行为型。

一、创建型模式

关注对象的创建方式。

1. 单例模式(Singleton)

确保一个类只有一个实例,并提供全局访问点。   控制实例数量,全局共享。

class Singleton {
  static instance:any;
  constructor() {
    if (Singleton.instance) return Singleton.instance;
    Singleton.instance = this;
  }
}
const a = new Singleton();
const b = new Singleton();
console.log(a === b); // true

2. 工厂方法模式(Factory Method)

不直接new,而是通过工厂生产

// 定义 Car 类,并为属性添加类型
class Car {
  model: string; // 明确 model 是字符串类型

  constructor(model: string) { // 构造函数参数也明确类型
    this.model = model;
  }
}

// 定义 CarFactory 类
class CarFactory {
  // 方法接收一个 string 类型的 model 参数,返回一个 Car 实例
  createCar(model: string): Car {
    return new Car(model);
  }
}

// 使用
const factory = new CarFactory();
const carA = factory.createCar('Toyota');
console.log(carA.model); // 输出: Toyota
 

3. 抽象工厂模式(Abstract Factory)

提供一个接口,用于创建相关或依赖对象的家族,而无需指定具体类。 创建一组相关对象。

interface Button {
    render(): void;
}

interface Checkbox {
    render(): void;
}

class WinButton implements Button {
    render() { console.log("Windows按钮"); }
}

class MacButton implements Button {
    render() { console.log("Mac按钮"); }
}

class WinCheckbox implements Checkbox {
    render() { console.log("WinCheckbox"); }
}


interface GUIFactory {
    createButton(): Button;
    createCheckbox(): Checkbox;
}

class WinFactory implements GUIFactory {
    createButton() { return new WinButton(); }
    createCheckbox() { return new WinCheckbox(); } // 假设有WinCheckbox
}
// 使用:
const factory = new WinFactory(); 
factory.createButton().render();

factory.createCheckbox().render();

4. 建造者模式(Builder)

分步骤构建复杂对象,灵活组合。

class BurgerBuilder {
  constructor() { this.burger = {}; }
  addLettuce() { this.burger.lettuce = true; return this; }
  addCheese() { this.burger.cheese = true; return this; }
  build() { return this.burger; }
}
const burger = new BurgerBuilder().addLettuce().addCheese().build();
console.log(burger);

5. 原型模式(Prototype)

通过复制现有对象来创建新对象,而不是新建实例。 克隆对象,避免重复初始化。

const vehicle = {
  wheels: 4,
  start() { console.log('Starting...'); }
};
const car = Object.create(vehicle);
car.color = 'red';
console.log(car.wheels); // 4

二、结构型模式

关注类和对象的组合方式。

6. 适配器模式(Adapter)

例子1:电压转化

美国电器110V,中国220V,就要有一个变压器将110V转化为220V

将一个类的接口转换成期望的另一个接口

// 定义V110Power类
class V110Power {
    supplyPower() {
        console.log("V110Power提供110V电源");
        return "110V电源";
    }
}

// 定义V220Power类(目标接口)
class V220Power {
    providePower() {
        console.log("V220Power提供220V电源");
        return "220V电源";
    }
}

// 定义适配器类,将V110Power适配为V220Power
class V220PowerAdapter extends V220Power {
    constructor(v110Power) {
        super(); // 调用父类构造函数
        this.v110Power = v110Power;
    }
    
    // 重写providePower方法,内部调用v110Power的supplyPower方法
    providePower() {
        console.log("适配器正在将110V电源转换为220V电源...");
        const power = this.v110Power.supplyPower();
        // 这里可以添加实际的转换逻辑(在现实中可能是变压器等)
        console.log(`将${power}转换为220V电源`);
        return "220V电源"; // 返回适配后的电源
    }
}

// 定义ChinaDevice类
class ChinaDevice {
    inputPower(power) {
        // 这里处理输入的电源
        console.log("ChinaDevice接收到了电源:", power.constructor.name);
        const powerProvided = power.providePower(); // 调用提供电源的方法
        console.log("设备正在使用:", powerProvided);
        // 实际应用中可能会有更多处理逻辑
    }
}

// 使用示例
const chinaDevice = new ChinaDevice();
const v110Power = new V110Power();
const v220PowerAdapter = new V220PowerAdapter(v110Power); // 创建适配器
chinaDevice.inputPower(v220PowerAdapter); // 传入适配后的电源
例子2:第三方支持接口统一
// 第三方支付类:PayPal
class PayPal {
  makePayment(amountInUSD) {
    console.log(`使用 PayPal 支付了 ${amountInUSD} 美元`);
  }
} 

// 第三方支付类:Stripe
class Stripe {
  charge(amountInCents) {
    console.log(`使用 Stripe 支付了 ${amountInCents / 100} 美元`);
  }
}

上面PayPal 和 Stripe支付的接口为单位都不一样

// 目标接口:统一的支付接口
class Payment {
  pay(amount) {
    throw new Error("该方法需要在子类中实现");
  }
}

// PayPal 适配器
class PayPalAdapter extends Payment {
  constructor(paypal) {
    super();
    this.paypal = paypal;
  }

  pay(amountInDollars) {
    // 将美元传给 PayPal 的 makePayment 方法
    this.paypal.makePayment(amountInDollars);
  }
}

// Stripe 适配器
class StripeAdapter extends Payment {
  constructor(stripe) {
    super();
    this.stripe = stripe;
  }

  pay(amountInDollars) {
    // 将美元转换为美分(1美元 = 100美分)
    const amountInCents = amountInDollars * 100;
    this.stripe.charge(amountInCents);
  }
}


// 客户端使用------

// 创建第三方支付实例
const paypal = new PayPal();
const stripe = new Stripe();

// 创建适配器实例
const paypalAdapter = new PayPalAdapter(paypal);
const stripeAdapter = new StripeAdapter(stripe);

// 使用适配后的支付接口进行支付 
paypalAdapter.pay(100) // 使用 PayPal 支付 100 美元
stripeAdapter.pay(50)  // 使用 Stripe 支付 50 美元

7. 桥接模式(Bridge)

将抽象部分与实现部分分离,使它们可以独立变化。 抽象与实现解耦。

把实例里面可以抽象的部分提取到抽象类。

class TV {
  turnOn() { console.log('TV on'); }
}
class Remote {
  constructor(device) { this.device = device; }
  power() { this.device.turnOn(); }
}
const remote = new Remote(new TV());
remote.power();

8. 组合模式(Composite)

将对象组合成树形结构以表示“部分-整体”层次。 统一处理单个对象和组合对象。

class File {
  constructor(name) { this.name = name; }
  display() { console.log(this.name); }
}
class Folder {
  constructor(name) {
    this.name = name;
    this.children = [];
  }
  add(item) { this.children.push(item); }
  display() {
    console.log(this.name);
    this.children.forEach(c => c.display());
  }
}
const root = new Folder('root');
root.add(new File('file1'));
const sub = new Folder('sub');
sub.add(new File('file2'));
root.add(sub);
root.display();

9. 装饰器模式(Decorator)

动态时在具体逻辑前植入新的逻辑,比继承更灵活。

function withTimestamp(fn) {
  return function(...args) {
    console.log('Timestamp:', Date.now());
    return fn(...args);
  };
}
const sayHello = withTimestamp(() => console.log('Hello'));
sayHello();

10. 外观模式(Facade)

为子系统提供统一的简化接口。把多个调用合并一个。

class CPU { start() { console.log('CPU Start'); } }
class Memory { load() { console.log('Memory Load'); } }
class Computer {
  constructor() {
    this.cpu = new CPU();
    this.memory = new Memory();
  }
  boot() {
    this.cpu.start();
    this.memory.load();
  }
}
const pc = new Computer();
pc.boot();

11. 享元模式(Flyweight)

通过缓存已经请求过的对象,提高下次访问速度。

const shapeFactory = (() => {
  const shapes = {};
  return {
    getCircle: color => {
      if (!shapes[color]) {
        shapes[color] = { color };
      }
      return shapes[color];
    }
  };
})();
const red1 = shapeFactory.getCircle('red');
const red2 = shapeFactory.getCircle('red');
console.log(red1 === red2); // true

12. 代理模式(Proxy)

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

const user = {
  name: 'John',
  age: 30
};
const proxy = new Proxy(user, {
  get(target, prop) {
    console.log(`Getting ${prop}`);
    return target[prop];
  }
});
console.log(proxy.name);

三、行为型模式

关注对象之间的交互和职责分配。

13. 责任链模式(Chain of Responsibility)*

将请求的发送者和接收者解耦,使多个对象都有机会处理请求。 链式处理请求。

function handler1(req, next) {
  if (req === 'ok') return 'Handled by 1';
  return next(req);
}
function handler2(req) {
  return 'Handled by 2';
}
function chain(req) {
  return handler1(req, handler2);
}
console.log(chain('error'));

14. 命令模式(Command)

将请求封装为对象,使你可以用不同的请求参数化其他对象。 将请求封装为对象。

class Light {
  on() { console.log('Light On'); }
  off() { console.log('Light Off'); }
}
class Command {
  constructor(light) { this.light = light; }
  execute() { this.light.on(); }
}
const cmd = new Command(new Light());
cmd.execute();

15. 解释器模式(Interpreter)

给定一种语言,定义它的文法表示,并定义一个解释器来解释该语言。 定义语言的文法规则。

const context = { A: true, B: false };
function interpret(expr) {
  return context[expr];
}
console.log(interpret('A')); // true

16. 迭代器模式(Iterator)

提供一种方法顺序访问聚合对象的元素,而不暴露其底层表示。    统一遍历方式。

const iterable = {
  data: [1, 2, 3],
  [Symbol.iterator]() {
    let i = 0;
    return {
      next: () => ({ done: i >= this.data.length, value: this.data[i++] })
    };
  }
};
for (let val of iterable) console.log(val);

17. 中介者模式(Mediator)*

用一个中介对象封装一系列对象交互,使对象之间松耦合。  集中控制交互。

class Mediator {
  send(message, sender) {
    this.receiver.receive(message, sender);
  }
}
class Colleague {
  constructor(name, mediator) {
    this.name = name;
    this.mediator = mediator;
  }
  send(msg) {
    this.mediator.send(msg, this.name);
  }
  receive(msg, sender) {
    console.log(`${this.name} got "${msg}" from ${sender}`);
  }
}
const mediator = new Mediator();
const c1 = new Colleague('A', mediator);
const c2 = new Colleague('B', mediator);
mediator.receiver = c2;
c1.send('Hello');

18. 备忘录模式(Memento)

在不破坏封装的前提下,捕获并外部化一个对象的内部状态以便恢复。 保存和恢复状态。

class Editor {
  constructor() {
    this.content = '';
    this.history = [];
  }
  write(text) {
    this.history.push(this.content);
    this.content += text;
  }
  undo() {
    this.content = this.history.pop();
  }
}
const editor = new Editor();
editor.write('Hello ');
editor.write('World');
editor.undo();
console.log(editor.content); // Hello 

19. 观察者模式(Observer)

定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都得到通知。 发布-订阅机制。

class Subject {
  constructor() { this.observers = []; }
  subscribe(fn) { this.observers.push(fn); }
  notify(data) { this.observers.forEach(fn => fn(data)); }
}
const subject = new Subject();
subject.subscribe(data => console.log('Got:', data));
subject.notify('Hello');

20. 状态模式(State)*

允许对象在其内部状态改变时改变其行为。 状态驱动行为。

class Light {
  constructor() {
    this.state = new OffState();
  }
  toggle() {
    this.state = this.state.toggle();
  }
}
class OffState {
  toggle() {
    console.log('Turning On');
    return new OnState();
  }
}
class OnState {
  toggle() {
    console.log('Turning Off');
    return new OffState();
  }
}
const light = new Light();
light.toggle(); // Turning On
light.toggle(); // Turning Off

21. 策略模式(Strategy)

定义一系列算法,封装每个算法,并使它们可以互换。

function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
function calculate(a, b, strategy) {
  return strategy(a, b);
}
console.log(calculate(3, 1, subtract)); // 2
console.log(calculate(3, 1, add)); 

22. 模板方法模式(Template Method)

定义一个操作中的算法骨架,将某些步骤延迟到子类中实现。 固定流程,可变步骤。主要继承实现

class Game {
  start() {
    this.init();
    this.play();
    this.end();
  }
  init() {}
  play() {}
  end() {}
}
class Chess extends Game {
  init() { console.log('Chess Init'); }
  play() { console.log('Chess Playing'); }
  end() { console.log('Chess End'); }
}
new Chess().start();

23. 访问者模式(Visitor)*

表示一个作用于某对象结构中的各元素的操作,使你可以在不改变各元素的类的前提下定义新的操作。   分离数据结构和操作。

class Animal {
  accept(visitor) {
    visitor.visit(this);
  }
}
class Visitor {
  visit(animal) {
    console.log('Visiting', animal.constructor.name);
  }
}
const animal = new Animal();
animal.accept(new Visitor());