Strategy-Pattern模式(策略模式)

策略模式(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角色去实现需求