定义
策略模式,是一种行为设计模式,它允许定义一些算法,把它们一个个封装起来,并使它们可以互换。
该模式让算法的变化独立于使用算法的客户端。这种模式特别适用于当你有多种类似的操作,但是它们之间有细微的差异时。
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");