设计模式-策略模式

32 阅读1分钟

定义

策略模式,是一种行为设计模式,它允许定义一些算法,把它们一个个封装起来,并使它们可以互换。

该模式让算法的变化独立于使用算法的客户端。这种模式特别适用于当你有多种类似的操作,但是它们之间有细微的差异时。

UML 类图

typescript 实现

1. 定义一个策略接口

interface Strategy {
  execute(data: number[]): number[];
}

2. 创建具体策略类

class ConcreteStrategyA implements Strategy {
  public execute(data: number[]): number[] {
    return data.sort();
  }
}

class ConcreteStrategyB implements Strategy {
  public execute(data: number[]): number[] {
    return data.reverse();
  }
}

3. 创建上下文类

class Context {
  private strategy: Strategy;
  
  constructor(strategy: Strategy) {
    this.strategy = strategy;
  }
  public setStrategy(strategy: Strategy): void {
    this.strategy = strategy;
  }
  public executeStrategy(data: number[]): number[] {
    return this.strategy.execute(data);
  }
}

4. 使用示例

const data = [1,3,4,2];

const context = new Context(new ConcreteStrategyA());
console.log("Client: Strategy is set to normal sorting.");
console.log(context.executeStrategy(data));

context.setStrategy(new ConcreteStrategyB());
console.log("Client: Strategy is set to reverse sorting.");
console.log(context.executeStrategy(data));

通用实现

javascript 不必使用类的方式来存放策略,函数即可

1. 函数方式

// 公共代码
export const StrategyRegistry = (function() {
  const handlers = new Map();
  function addHandler(kind: string, handler: (...args: any[]) => any) {
    handlers.set(kind, handler);
  }
  function handle(kind: string, ...args: any[]) {
    if(!handlers.get(kind)) {
      throw new Error(`No handler registered for ${kind}`);
    }
    return handlers.get(kind)(...args);
  }
  return { addHandler, handle };
})();

// 私有代码,使用示例
StrategyRegistry.addHandler("video", (...args: any[]) => {
  consoel.log("handle video", args);
});
StrategyRegistry.addHandler("audio", (...args: any[]) => {
  console.log("handle audio", args);
});

StrategyRegistry.handle("video", "h264", "vp8");
StrategyRegistry.handle("audio", "aac", "opus");

2. 类方式

export class StrategyRouter<T extends Strategy> {
  private handlers: Map<string, (strategy: T) => any>;
  constructor() {
    this.handlers = new Map();
  }
  addHandler(kind: string, handler: (strategy: T) => any) {
    this.handlers.set(kind, handler);
  }
  handle(kind: string, strategy: T) {
    const handler = this.handlers.get(kind);
    if(handler) {
      handler(strategy);
    } else {
      throw new Error(`No handler registered for ${kind}`);
    }
  }
}
export abstract class Strategy<T extends Strategy = any> {
  protected router: StrategyRouter<T> | undefined;
  static createStrategyRouter<T extends Strategy>(
    this: new (router: StrategyRouter<T>) => T
  ): StrategyRouter<T> {
    return new StrategyRouter();
  }
  protected constructor(router?: StrategyRouter<T>) {
    this.router = router;
  }
  addRouter(router: StrategyRouter<T>) {
    this.router = router;
  }
  handle(kind: string) {
    this.router?.handle(kind, this as unknown as T);
  }
}

// 私有代码,使用示例
class Media extends Strategy {
  public codec: string;
  
  constructor(router: StrategyRouter<Media>) {
    super(router);
    this.codec = "aac";
  }
}

let router = Media.createStrategyRouter();
router.addHandler("video", (strategy: Media) => {
  console.log("handle video, codec is", strategy.codec);
});

router.addHandler("audio", (strategy: Media) => {
  console.log("handle audio, codec is", strategy.codec);
});

const media = new Media(router);
media.handle("audio");