策略模式(Strategy Pattern) :一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
介绍
意图: 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决: 在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
何时使用: 一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决: 将这些算法封装成一个一个的类,任意地替换。
关键代码: 实现同一个接口。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
示例 Hand.ts 表示猜拳游戏中的手势类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 export default class Hand { public HANDVALUE_GUU = 0;//表示石头的值 public HANDVALUE_CHO = 1;//表示剪刀的值 public HANDVALUE_PAA = 2;//表示布的值 private name: Array<string> = ["石头", "剪刀", "布"]; private handValue: number; constructor(handValue: number) { this.handValue = handValue; } public getHand(handValue: number): Hand { return new Hand(handValue); } public isStrongerThan(h: Hand): boolean { return this.fight(h) == 1; } public isWeakerThan(h: Hand): boolean { return this.fight(h) == -1; } private fight(h: Hand): number { if (this.handValue == h.handValue) { return 0; } else if ((this.handValue + 1) % 3 > h.handValue) { return 1; } else { return -1; } } public toString(): string { return this.name[this.handValue]; } }
Strategy.ts 表示猜拳游戏中的策略的类 1 2 3 4 5 6 import Hand from "./Hand"; export interface Strategy { nextHand(): Hand; study(win: boolean): void; }
WinningStrategy.ts 表示如果这局获胜,那么下一局同样手势的类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { Strategy } from "./type"; import Hand from "./Hand"; export default class WinningStrategy implements Strategy { private win: boolean = false; private prevHand: Hand; constructor() { } public nextHand(): Hand { if (!this.win) { const random = Math.floor(Math.random() * 3); const hand = new Hand(random); this.prevHand = hand.getHand(random); } return this.prevHand; } public study(win: boolean) { this.win = win; } }
RandomStragegy.ts 表示每次都随机手势 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { Strategy } from "./type"; import Hand from "./Hand"; export default class RandomStragegy implements Strategy { constructor() { } public nextHand(): Hand { const random = Math.floor(Math.random() * 3); const hand = new Hand(random); return hand.getHand(random); } public study(win: boolean) { } }
Player.ts 猜拳游戏的选手类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import { Strategy } from "./type"; import Hand from "./Hand"; export default class Play { private name: string; private winCount: number = 0; private loseCount: number = 0; private gameCount: number = 0; private stragegy: Strategy constructor(name: string, stragegy: Strategy) { this.name = name; this.stragegy = stragegy; } public nextHand(): Hand { return this.stragegy.nextHand() } public win(): void { this.stragegy.study(true); this.winCount++; this.gameCount++; } public lose(): void { this.stragegy.study(false); this.loseCount++; this.gameCount++; } public even() { this.gameCount++; } public toString(): string { return `[${this.name}:${this.gameCount} games,${this.winCount} win,${this.loseCount} lose]` } }
index.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import Play from "./Play"; import WinningStrategy from "./WinningStrategy"; import RandomStragegy from "./RandomStragegy"; const play1: Play = new Play("Trao", new WinningStrategy());//坚持上一次赢 const play2: Play = new Play("Haha", new RandomStragegy());//随机 for (let i = 0; i < 20; i++) { const nextHand1 = play1.nextHand(); const nextHand2 = play2.nextHand(); if (nextHand1.isStrongerThan(nextHand2)) { console.log(`Winner ${play1.toString()} ${nextHand1.toString()}`); play1.win(); play2.lose(); } else if (nextHand2.isStrongerThan(nextHand1)) { console.log(`Winner ${play2.toString()} ${nextHand2.toString()}`); play2.win(); play1.lose(); } else { console.log(`Even... ${nextHand1.toString()}`); play2.even(); play1.even(); } }
result 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Even... 布 Even... 布 Even... 布 Even... 剪刀 Winner [Haha:5 games,1 win,0 lose] 剪刀 Winner [Trao:6 games,1 win,1 lose] 剪刀 Even... 剪刀 Even... 剪刀 Even... 剪刀 Winner [Trao:10 games,2 win,1 lose] 剪刀 Even... 剪刀 Winner [Trao:12 games,3 win,1 lose] 剪刀 Even... 剪刀 Winner [Trao:14 games,4 win,1 lose] 剪刀 Winner [Trao:15 games,5 win,1 lose] 剪刀 Even... 剪刀 Even... 剪刀 Winner [Trao:18 games,6 win,1 lose] 剪刀 Winner [Trao:19 games,7 win,1 lose] 剪刀 Even... 剪刀 ————————————Result———————————————— Result [Trao:20 games,7 win,1 lose] Result [Haha:20 games,1 win,7 lose]
类图
角色
Strategy(策略) Strategy角色决定实现策略所必须的接口
ConcreteStrategy(具体的策略) ConcreteStrategy角色负责实现Strategy角色的接口,即负责实现具体的策略(方法、算法)。
Context(上下文) 负责使用Strategy角色。Context角色保存ConcreteStrategy角色的实例,彬使用ConcreteStrategy角色去实现需求