一、理论
- Strategy(策略)
Strategy角色负责决定实现策略所必需的接口(API)。 - ConcreteStrategy(具体策略)
ConcreteStrategy角色负责实现Strategy角色的接口(API),即负责实现具体的策略(战略、方向、方法和算法)。
-Context(上下文)
负责使用Strategy角色。Context角色保存了ConcreteStrategy 角色的实例,并使用ConcreteStrategy角色去实现需求(总之,还是要调用Strategy 角色的接口(API))。
二、代码
模拟一个剪刀石头布的游戏,两个选手使用不同的策略:
public class Hand {
private int handValue;
public Hand(int value) {
this.handValue = value;
}
public static final int HAND_ROCK = 0;
public static final int HAND_PAPER = 1;
public static final int HAND_SCISSORS = 2;
public static final Hand[] HANDS = {new Hand(HAND_ROCK), new Hand(HAND_PAPER), new Hand(HAND_SCISSORS)};
private static final String[] names = {"石头", "布", "剪刀"};
public Hand getHand(int value) {
return HANDS[value];
}
public boolean isWin(Hand hand) {
return fight(hand) == 1;
}
public boolean isLose(Hand hand) {
return fight(hand) == -1;
}
private int fight(Hand hand) {
if (this.handValue == hand.handValue) {
return 0;
} else if ((this.handValue + 1) % 3 == hand.handValue) {
return -1;
} else {
return 1;
}
}
@Override
public String toString() {
return names[handValue];
}
}
Strategy(策略):
public interface IStrategy {
abstract Hand nextHand();
abstract void study(boolean win);
}
ConcreteStrategy(具体策略):
public class WinningStrategy implements IStrategy {
private Hand prev = new Hand(new Random().nextInt(3));
private boolean win = false;
@Override
public Hand nextHand() {
if (win) {
return prev;
}
return prev.getHand(new Random().nextInt(3));
}
@Override
public void study(boolean win) {
this.win = win;
}
}
public class HistoryStrategy implements IStrategy {
private int prevValue = new Random().nextInt(3);
private int currentValue = 0;
private int[][] history = new int[3][3];
@Override
public Hand nextHand() {
int temp = new Random().nextInt(getSum(currentValue));
int handValue = 0;
if (temp < history[currentValue][0]) {
handValue = 0;
} else if (temp < history[currentValue][0] + history[currentValue][1]) {
handValue = 1;
} else {
handValue = 2;
}
return Hand.HANDS[handValue];
}
private int getSum(int handValue) {
int sum = 1;
for (int i = 0; i < history[handValue].length; i++) {
sum += history[handValue][i];
}
return sum;
}
@Override
public void study(boolean win) {
if (win) {
history[prevValue][currentValue]++;
} else {
history[prevValue][(currentValue + 1) % 3]++;
history[prevValue][(currentValue + 2) % 3]++;
}
}
}
Context(上下文):
@Data
@ToString(exclude = "strategy")
public class Player {
private String name;
private IStrategy strategy;
private int loseCount;
private int winCount;
private int evenCount;
private int gameCount;
public Player(String name, IStrategy strategy) {
this.name = name;
this.strategy = strategy;
}
public Hand nextHand() {
return strategy.nextHand();
}
public void win() {
strategy.study(true);
winCount++;
gameCount++;
}
public void lose() {
strategy.study(false);
loseCount++;
gameCount++;
}
public void even() {
evenCount++;
gameCount++;
}
}
测试:
public class Client {
public static void main(String[] args) {
Player player1 = new Player("张三", new WinningStrategy());
Player player2 = new Player("张三", new HistoryStrategy());
for (int i = 0; i < 10; i++) {
Hand p1hand = player1.nextHand();
Hand p2Hand = player2.nextHand();
if (p1hand.isWin(p2Hand)) {
player1.win();
player2.lose();
} else if (p1hand.isLose(p2Hand)) {
player1.lose();
player2.win();
} else {
player1.even();
player2.even();
}
}
System.out.println(player1);
System.out.println(player2);
}
}
三、总结
- 策略模式的关键是:分析项目中变化部分与不变部分
- 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
- 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可,避免了使用多重转移语句(if…else if…else)
- 提供了可以替换继承关系的办法: 策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展
- 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞