策略(Strategy)模式-----------整体地替换算法

》》Strategy  的意思是“策略”,指的是与敌军对垒时行军作战的方法。在

       编程中,我们可以将它理解为“算法”。

》》使用Strategy 可以整体地替换算法的实现部分。能够整体地替换算法,

        能让我们轻松地以不同的算法去解决同一个问题,这种模式就是

        Strategy 模式。

----------------------下面的示例程序的功能是:让电脑玩“猜拳”游戏。

        我们考虑了两种猜拳的策略,第一种策略是:“如果这局猜拳获胜,那

     么下一局也出一样的手势”,这是一种稍微有些笨的策略;另外一种策略

     是“根据上一局的手势从概率上计算出下一局的手势”

》》示例程序的类图:

       

》》Hand  类:

        package strategy;


public class Hand {
public static final int HANDVALUE_GUU = 0;   //表示石头的值
public static final int HANDVALUE_CHO = 1;   //表示剪刀的值
public static final int HANDVALUE_PAA = 2;   //表示布的值
private static final String[] name = {"石头",  //表示猜拳中手势所对应的字符串
"剪刀","布"};

private int handvalue;                       //猜拳中出的手势的值
//构造函数
private Hand(int handvalue){
this.handvalue = handvalue;
}
//表示猜拳中 3 种手势的实例
public static final Hand[] hand = {         
new Hand(HANDVALUE_GUU),
new Hand(HANDVALUE_CHO),
new Hand(HANDVALUE_PAA)
};
//根据手势的值获取其对应的实例
public static Hand getHand(int handvalue){
return hand[handvalue];
}

//计分:平 0 , 胜1 , 负 -1
private int fight(Hand h){
if(this == h){
return 0;
}else if((this.handvalue+1)%3 == h.handvalue){
return 1;
}else{
return -1;
}
}
//如果 this 获胜了 h ,则返回 true 
public boolean isStrongerThan(Hand h){
return fight(h)== 1;
}

//如果 this 输给了 h 则返回 true 
public boolean isWeakerThan(Hand h){
return fight(h) == -1;
}
//转换为手势值所对应的字符串 
public String toString(){
return name[handvalue];
}



}

  说明:虽然 Hand 类会被其他类(Player类、WinningStrategy类、

  ProbStrategy 类)使用,但它并非 Strategy 模式中的角色。

》》Strategy 接口:

         package strategy;
/*
 * 该接口定义了猜拳策略的抽象方法的接口
 */
public interface Strategy {
/*
* 下面方法的作用是:“获取下一局要出的手势”
*/
public abstract Hand nextHand();
/*
* 下面方法的作用是学习“上一局的手势是否获胜了”
*/
public abstract void study(boolean win);


}

》》WinningStrategy 类:

         package strategy;
import java.util.Random;
/*
 * 该类是第一种猜拳策略:如果上一局的手势获胜了,则下一局的手势就与
 * 上一局相同(上一局出石,下一局继续出石头;上一局出布,下一局继续出布)。
 * 如果上一局的手势输了,则下一局就随机出手势。
 */


public class WinningStrategy implements Strategy {
//随机数生成器
private Random random;
//保存上一局猜拳的输赢结果。如果上一局猜拳获胜了,则 won 值为 true ;
//如果上一局输了,则won 的值为 false 
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;


}


}

》》ProbStrategy 类:(此策略的大前提是对方只有一种猜拳模式)

       package strategy;
import java.util.Random;


public 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);
}

private int getSum(int hv){
int sum = 0;
for(int i = 0 ; i < 3 ; i++ ){
sum += history[hv][i];
}
return sum;

}


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.hand[handvalue];
}


     
public void study(boolean win) {
if(win){
history[prevHandValue][currentHandValue]++;
}else{
history[prevHandValue][(currentHandValue+1)%3]++;
history[prevHandValue][(currentHandValue+2)%3]++;

}



}


}

      说明:上面程序中的 history 字段是一个表,被用于根据过去的胜负

        来进行概率计算。它是一个二维数组,每个数组下标的意思如下:

        history[上一局出的手势] [这一局出的手势]

       这个表达式的值越大,表示过去的胜率就越高。下面稍微详细讲解下:

        假如我们上一局出的是石头:

             history[0][0]         两局分别出石头、石头时胜了的次数

             history[0][1]         两局分别出石头、剪刀时胜了的次数

             history[0][2]         两局分别出石头、布时胜了的次数

         根据上面三个表达式的值从概率上计算出下一局出什么。简而言之,

          就是先计算出这三个表达式的值的和,然后再从0 到这个和之间取

         一个随机数,并据此决定下一局应该出什么手势。

              例如:如果

             history[0][0]   是 3

             history[0][1]   是5

             history[0][2]   是 7

             然后在 0 至15 (不含15 , 15 是 3+5+7的和)之间随机取一个数。

        如果随机数在 0 至 3 (不含3)之间,那么出石头;

        如果随机数在 3 至8(不含8)之间,那么出剪刀;

         如果随机数在 8 至 15 (不含15) 之间,那么出布


         study 方法会根据 nextHand 方法返回的手势的胜负结果来更新 history 

     字段中的值。

》》Player 类:

        package strategy;
/*
 * 该类表示的是进行猜拳游戏的选手的类。
 * 在生成实例时,需要向其传递“姓名”和“策略”
 */


public 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++;
}

public String toString(){
return "["+name+":"+gamecount+"  games, "+wincount+" win , "+
           losecount+" lose "+"]";
}


}

》》Main 类:

        package strategy;


public class Main {
public static void main(String[] args){
if(args.length !=2){
System.out.println("Usage:java Main randomseed1 randomseed2");
System.out.println("Example:java Main 314 15");
System.exit(0);
}
int seed1 = Integer.parseInt(args[0]);
int seed2 = Integer.parseInt(args[1]);
Player player1 = new Player("Taro",new WinningStrategy(seed1));
Player player2 = new Player("Hana",new ProbStrategy(seed2));

for(int i = 0 ; i < 50 ;i++ ){
Hand nextHand1 = player1.nextHand();
Hand nextHand2 = player2.nextHand();
if(nextHand1.isStrongerThan(nextHand2)){
System.out.println("Winner:"+player1);
player1.win();
player2.lose();
}else if(nextHand1.isWeakerThan(nextHand2)){
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());

}


}

           说明:上面的 Main 类是测试程序,负责使用以上类让电脑进行

           猜拳游戏。该测试程序中,让两位选手进行  50 场游戏,然后显示

           比赛结果。

---------------------------------------------------------------------------------------------

 》》上面测试程序的运行结果:

          

----------------------------------------------------------------------------------

                   《Strategy 模式中的登场角色》

            ****  Strategy (策略)

                      Strategy 角色负责决定实现策略所必需的接口(API)。在示例程序中,由

                  Strategy 接口扮演此角色。

            **** ConcreteStrategy (具体的策略)

                      ConcreteStrategy 角色负责实现 Strategy 角色的接口,即负责实现具体的策略

                 (战略、方向、方法和算法)。在示例程序中,由 WinningStrategy 类和

                  ProbStrategy 类扮演此角色。

            ****  Context (上下文)

                      负责使用 Strategy 角色。Context 角色保存了 ConcreteStrategy 角色的实例,

                 并使用 ConcreteStrategy 角色去实现需求(总之,还是要调用 Strategy 角色

                 的接口)。在示例程序中,由Player 类扮演此角色。

------------------------------------------------------------------------------------

                      《扩展思路的要点》

                    1、为什么需要特意编写 Strategy 角色

                            通常在编程时算法会被写在具体方法中。Strategy 模式却特意将算法与其他

                      部分分离开来,只是定义了与算法相关的接口,然后在程序中以委托的方式来

                     使用算法。

                            当我们想要通过改善算法来提高算法的处理速度时,如果使用了 Strategy 

                      模式 ,就不必修改 Strategy 角色的接口了,仅仅修改ConcreteStrategy 角色

                      即可。而且,使用委托这种弱关联关系可以很方便地整体替换算法

                            使用Strategy 模式编写象棋程序时,可以方便地根据选手的选择切换

                      AI 例程的水平。

                    2、程序运行中也可以切换策略

                            如果使用  Strategy 模式,在程序运行中也可以切换 ConcreteStrategy 角色。

                      例如,在内存容量少的运行环境中可以使用  SlowButLessMemoryStrategy

                      (速度慢但省内存的策略)而在内存容量多的运行环境中则可以使用

                       FastButMoreMemoryStrategy (速度快但是耗内存的策略)

                             此外,还可以用某种算法去“验算”另外一种算法。例如,假设要在某个表格

                      计算软件的开发版本中进行复杂的计算。这时,我们可以准备两种算法,即

                      “高速但计算上可能有 bug 的算法”和“低速但计算准确的算法”,然后让后者去

                      验证前者的计算结果。

----------------------------------------------------------------

                     《相关的设计模式》

                    **** Flyweight 模式

                    **** Abstract Factory 模式

                    **** State 模式

猜你喜欢

转载自blog.csdn.net/lierming__/article/details/79689202