设计模式-Typescript示例(上)

98 阅读1分钟

设计模式是在软件开发中针对常见问题总结出来的解决方案。它们不是代码,而是一些经过验证的设计思想和最佳实践。下面是对每种模式的概述和实现:

策略模式

定义算法族,并将它们分别封装起来,让它们之间可以相互替换。

// 策略模式

interface PaymentMethod {
  pay(amount: number): void;
}

class WeChat implements PaymentMethod {
  pay(amount: number): void {
    console.log(`Pay ${amount} via WeChat`);
  }
}

class Alipay implements PaymentMethod {
  pay(amount: number): void {
    console.log(`Pay ${amount} via Alipay`);
  }
}

class Credit implements PaymentMethod {
  pay(amount: number): void {
    console.log(`Pay ${amount} via Credit`);
  }
}

class MTWallet implements PaymentMethod {
  pay(amount: number): void {
    console.log(`Pay ${amount} via MT wallet`);
  }
}

class ELMWallet implements PaymentMethod {
  pay(amount: number): void {
    console.log(`Pay ${amount} via ELM wallet`);
  }
}

abstract class TaskAway {
  private paymentMethod: PaymentMethod | undefined;

  constructor(method?: PaymentMethod) {
    this.paymentMethod = method;
  }

  pay(amount: number) {
    if (!this.paymentMethod) {
      throw new Error('No payment method specified');
    }

    this.paymentMethod.pay(amount);
  }

  selectPaymentMethod(method: PaymentMethod) {
    this.paymentMethod = method;
    return this;
  }

  abstract pickFood(): void;
}

class MT extends TaskAway {
  constructor() {
    super(new MTWallet());
  }

  pickFood(): void {
    console.log('pick food on the MT platform');
  }
}

class ELM extends TaskAway {
  constructor() {
    super(new ELMWallet());
  }

  pickFood(): void {
    console.log('pick food on the ELM platform');
  }
}

const mt = new MT();
mt.pay(9.9);
mt.selectPaymentMethod(new WeChat()).pay(18.3);

const elm = new MT();
elm.selectPaymentMethod(new Alipay()).pay(29.9);

观察者模式

定义对象之间的一对多依赖,当对象状态变更时,它的依赖者们都会收到通知。

// 观察者模式

interface Listener {
  (...args: any[]): void;
}

class MyEventEmitter {
  private listenersGroupByEventName: { [key: string]: Listener[] };

  constructor() {
    this.listenersGroupByEventName = {};
  }

  on(eventName: string, listener: Listener): boolean {
    if (this.listenersGroupByEventName[eventName]) {
      this.listenersGroupByEventName[eventName].push(listener);
    } else {
      this.listenersGroupByEventName[eventName] = [listener];
    }

    return true;
  }

  off(eventName: string, listener: Listener): boolean {
    if (!this.listenersGroupByEventName[eventName]) {
      return false;
    }

    const index = this.listenersGroupByEventName[eventName].findIndex((cb) => cb === listener);
    if (index < 0) {
      return false;
    }

    this.listenersGroupByEventName[eventName].splice(index, 1);

    return true;
  }

  emit(eventName: string, ...args: any[]): void {
    if (this.listenersGroupByEventName[eventName]) {
      this.listenersGroupByEventName[eventName].forEach((listener) => listener(...args));
    }
  }
}

const myEmitter = new MyEventEmitter();

const listener1 = (...args: any[]) => console.log(`listener1 received: ${args}`);

const listener2 = (...args: any[]) => console.log(`listener2 received: ${args}`);

myEmitter.on('en1', listener1);
myEmitter.on('en1', listener2);
myEmitter.on('en2', listener1);
myEmitter.emit('en1', 1, 2, 3);
myEmitter.emit('en2', 4, 5, 6);
myEmitter.emit('not Exists', 7, 8, 9);

myEmitter.off('en1', listener2);
myEmitter.emit('en1', 'hello world');

简单工厂

不是一种设计模式,但是比较常用。

// 简单工厂写法

abstract class MessageDelegate {
  formatContent(content: string): string {
    return content;
  }

  abstract sendMessage(toUserId: string, content: string): void;
}

class SMS extends MessageDelegate {
  sendMessage(toUserId: string, text: string): void {
    console.log(`Send sms to ${toUserId}: ${text}`);
  }
}

class Email extends MessageDelegate {
  formatContent(content: string): string {
    return `<b>${content}</b>`
  }

  sendMessage(toUserId: string, body: string): void {
    console.log(`Send email to ${toUserId}: ${body}`);
  }
}

class SimpleMessageDelegateFactory {
  static createMessageDelegate(messagePlatform: string): MessageDelegate {
    switch (messagePlatform) {
      case 'sms':
        return new SMS();
      case 'email':
        return new Email();
      default:
        throw new Error('Unknown message platform')
    }
  }
}

class BroadCast {
  send(messagePlatform: string, toUserId: string, messageContent: string): void {
    const messageDelegate = SimpleMessageDelegateFactory.createMessageDelegate(messagePlatform);

    const formattedContent = messageDelegate.formatContent(messageContent);

    messageDelegate.sendMessage(toUserId, formattedContent);
  }
}

const b = new BroadCast();
b.send('sms', '10086', '1');
b.send('email', '1234@qq.com', 'Apply for NodeJs Development Engineer');

工厂模式

定义了一个创建接口,但由子类具体决定要实例化的类是哪一个。

相比于简单工厂,可以通过继承Broadcast类,重载send函数执行逻辑。

// 工厂模式

abstract class MessageDelegate {
  formatContent(content: string): string {
    return content;
  }

  abstract sendMessage(toUserId: string, content: string): void;
}

class SMS extends MessageDelegate {
  sendMessage(toUserId: string, text: string): void {
    console.log(`Send sms to ${toUserId}: ${text}`);
  }
}

class Email extends MessageDelegate {
  formatContent(content: string): string {
    return `<b>${content}</b>`
  }

  sendMessage(toUserId: string, body: string): void {
    console.log(`Send email to ${toUserId}: ${body}`);
  }
}

class BroadCast {
  send(messagePlatform: string, toUserId: string, messageContent: string): void {
    const messageDelegate = this.createMessageDelegate(messagePlatform);

    const formattedContent = messageDelegate.formatContent(messageContent);

    messageDelegate.sendMessage(toUserId, formattedContent);
  }

  createMessageDelegate(messagePlatform: string): MessageDelegate {
    switch (messagePlatform) {
      case 'sms':
        return new SMS();
      case 'email':
        return new Email();
      default:
        throw new Error('Unknown message platform')
    }
  }
}

class BroadCastWithLogTracer extends BroadCast {
  send(messagePlatform: string, toUserId: string, messageContent: string): void {
    console.info(`Start sending ${messagePlatform}...`);

    const messageDelegate = this.createMessageDelegate(messagePlatform);

    const formattedContent = messageDelegate.formatContent(messageContent);

    messageDelegate.sendMessage(toUserId, formattedContent);

    console.info(`Send ${messagePlatform} completed!!!`);
  }
}

const b = new BroadCastWithLogTracer();
b.send('sms', '10086', '1');
b.send('email', '1234@qq.com', 'Apply for NodeJs Development Engineer');

抽象工厂模式

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

// 抽象工厂模式

interface Button {
  render(): void;
}

interface Input {
  render(): void;
}

interface LoginUIFactory {
  createLoginButton(): Button;
  createAccountInput(): Input;
  createPasswordInput(): Input;
}

class AndroidLoginButton implements Button {
  render(): void {
    console.log('Render a android style login button');
  }
}

class IOSLoginButton implements Button {
  render(): void {
    console.log('Render a ios style login button');
  }
}

class AndroidAccountInput implements Input {
  render(): void {
    console.log('Render a android style account input');
  }
}

class IOSAccountInput implements Input {
  render(): void {
    console.log('Render a ios style account input');
  }
}

class AndroidPasswordInput implements Input {
  render(): void {
    console.log('Render a android style password input');
  }
}

class IOSPasswordInput implements Input {
  render(): void {
    console.log('Render a ios style password input');
  }
}

class AndroidLoginUIFactory implements LoginUIFactory {
  createLoginButton(): Button {
    return new AndroidLoginButton();
  }

  createAccountInput(): Input {
    return new AndroidAccountInput();
  }

  createPasswordInput(): Input {
    return new AndroidPasswordInput();
  }
}

class IOSLoginUIFactory implements LoginUIFactory {
  createLoginButton(): Button {
    return new IOSLoginButton();
  }

  createAccountInput(): Input {
    return new IOSAccountInput();
  }

  createPasswordInput(): Input {
    return new IOSPasswordInput();
  }
}

class LoginPage {
  private loginButton: Button;
  private accountInput: Input;
  private passwordInput: Input;

  constructor(factory: LoginUIFactory) {
    this.loginButton = factory.createLoginButton();
    this.accountInput = factory.createAccountInput();
    this.passwordInput = factory.createPasswordInput();
  }

  render(): void {
    this.loginButton.render();
    this.accountInput.render();
    this.passwordInput.render();
  }
}

const iosLoginPage = new LoginPage(new IOSLoginUIFactory());
iosLoginPage.render();

const androidLoginPage = new LoginPage(new AndroidLoginUIFactory());
androidLoginPage.render();

装饰者模式

动态地向对象添加新功能。

// 装饰者模式

abstract class Coffee {
  abstract cost(): number;

  abstract getDescription(): string;
}

abstract class Condiment extends Coffee {
  coffee: Coffee;

  constructor(coffee: Coffee) {
    super();
    this.coffee = coffee;
  }
}

class Espresso extends Coffee {
  getDescription(): string {
    return 'americano';
  }

  cost(): number {
    return 10;
  }
}

class Ice extends Condiment {
  cost(): number {
    return 0.5 + this.coffee.cost();
  }

  getDescription(): string {
    return `${this.coffee.getDescription()}, Ice`;
  }
}

class Milk extends Condiment {
  cost(): number {
    return 1.5 + this.coffee.cost();
  }

  getDescription(): string {
    return `${this.coffee.getDescription()}, Milk`;
  }
}

class MilkFoam extends Condiment {
  cost(): number {
    return 3 + this.coffee.cost();
  }

  getDescription(): string {
    return `${this.coffee.getDescription()}, MilkFoam`;
  }
}

const espresso = new Espresso();
console.log(espresso.cost());
console.log(`${espresso.getDescription()}: ${espresso.cost()}¥`);

const cappuccino = new MilkFoam(new Milk(new Ice(new Espresso())));
console.log(`${cappuccino.getDescription()}: ${cappuccino.cost()}¥`);