策略模式(整体地替换算法)
用处
整体替换算法,提高扩展性。
角色
- Strategy(策略)
该角色负责决定实现策略所必须地方法(API)。 - ConcreteStrategy(具体的策略)
- 该角色负责实现Strategy角色地方法(API),即实现具体地策略(算法)。
- Context(上下文)
负责使用Strategy角色,Context角色保存ConcreteStrategy角色地实例,并使用ConcreteStrategy角色去实现需求。
类图
策略模式的类图非常简单
- Context类通过聚合Strategy接口,能够使用不同ConcreteStrategy类的策略
举例
public class Main {
public static void main(String[] args) {
Player player1 = new Player("Taro",new WinningStrategy(123));
Player player2 = new Player("Hana",new ProbStrategy(321));
for(int i = 0 ; i < 10000;i++){
Hand nextHand1 = player1.nextHand();
Hand nextHand2 = player2.nextHand();
if(nextHand1.isStrongerThan(nextHand2)){
System.out.println("Winner:"+player1);
player1.win();
player2.lose();
}else if(nextHand2.isStrongerThan(nextHand1)){
System.out.println("Winner:"+player2);
player1.lose();
player2.win();
}else{
System.out.println("Even...");
player1.even();
player2.even();
}
System.out.println("Total result:");
System.out.println(player1.toString());
System.out.println(player2.toString());
}
}
}
class Hand{
public static final int HANDVALUE_GUU = 0;
public static final int HANDVALUE_CHO = 1;
public static final int HANDVALUE_PAA = 2;
public static final Hand[] hand = {
new Hand(HANDVALUE_GUU),
new Hand(HANDVALUE_CHO),
new Hand(HANDVALUE_PAA),
};
private static final String[] name = {
"石头","剪刀","布"
};
private int handvalue;
private Hand(int handvalue){
this.handvalue = handvalue;
}
public static Hand getHand(int handvalue){
return hand[handvalue];
}
public boolean isStrongerThan(Hand h){
return fight(h) == 1 ;
}
public boolean isWeakerThan(Hand h){
return fight(h) == -1;
}
private int fight(Hand h){
if(this == h){
return 0;
}else if((this.handvalue + 1) % 3 == h.handvalue){
return 1;
}else{
return -1;
}
}
public String toString (){
return name[handvalue];
}
}
//Strategy角色
interface Strategy{
Hand nextHand();
void study(boolean win);
}
//ConcreteStrategy角色
class WinningStrategy implements Strategy{
private Random random;
private boolean won = false;
private Hand prevHand;
public WinningStrategy(int seed){
random = new Random(seed);
}
public Hand nextHand(){
if(! won){
prevHand = Hand.getHand(random.nextInt(3));
}
return prevHand;
}
public void study(boolean win){
won = win;
}
}
//ConcreteStrategy角色
class ProbStrategy implements Strategy {
private Random random;
private int prevHandValue = 0;
private int currentHandValue = 0;
private int[][] history = {
{1, 1, 1},
{1, 1, 1},
{1, 1, 1},
};
public ProbStrategy(int seed){
random = new Random(seed);
}
public Hand nextHand() {
int bet = random.nextInt(getSum(currentHandValue));
int handvalue = 0;
if (bet < history[currentHandValue][0]) {
handvalue = 0;
} else if (bet < history[currentHandValue][0] + history[currentHandValue][1]) {
handvalue = 1;
} else {
handvalue = 2;
}
prevHandValue = currentHandValue;
currentHandValue = handvalue;
return Hand.getHand(handvalue);
}
private int getSum(int hv) {
int sum = 0;
for (int i = 0; i < 3; i++) {
sum += history[hv][i];
}
return sum;
}
public void study(boolean win) {
if (win) {
history[prevHandValue][currentHandValue]++;
} else {
history[prevHandValue][(currentHandValue + 1) % 3]++;
history[prevHandValue][(currentHandValue + 2) % 3]++;
}
}
}
//Context角色
class Player{
private String name;
private Strategy strategy;
private int wincount;
private int losecount;
private int gamecount;
public Player(String name,Strategy 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(){
gamecount++;
}
@Override
public String toString() {
return "["+name+":"+gamecount+" games, "+wincount +" win, "+losecount+" lose"+"]";
}
}
举例程序较为复杂,但是核心部分非常简单
- Player类(Context角色)的业务逻辑中,调用Strategy接口的抽象方法,通过在Player类聚合不同的策略具体实现类(如ProbStrategy类和WinningStrategy),来让Player使用不同的策略。
总结
- 符合开闭原则
- 使用委托(弱关联),能灵活替换不同策略