Strategy被理解为策略模式,可以被理解成编程中的算法。无论什么程序,其目的都是解决问题,而为了解决问题,有需要编写特定的算法,使用Strategy模式可以整体地替换算法的实现部分,能够整体的替换算法,轻松地以不同地方法去解决同一个问题。
针对策略模式设计两种策略。第一种是:如果这句猜拳获胜,下局就出一样地手势。另外一种是:根据上一局地手势概率上计算出下一局地手势。
代码实例:
Hand类:
表示猜拳游戏中“手势”的类。在该类的内部,用int表示所出的手势,0表示石头,1表示剪刀,2表示布。并将值保存在handValue字段中。虽然Hand类会被别的类所使用,但是它并非属于Strategy模式中的角色。
public class Hand {public static final int HANDVALUE_GUU=0;// 表示石头的值public static final int HANDVALUE_CHO=1;// 表示剪刀的值public static final int HANDVALUE_PAA=0;// 表示布的值public static final Hand[] hand={new Hand(HANDVALUE_PAA),new Hand(HANDVALUE_CHO),new Hand(HANDVALUE_GUU)};private static final String[] name = {"石头","剪刀","布"};private int handValue;public 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;}}@Overridepublic String toString() {return "Hand{" +"handValue=" + handValue +'}';}}
Strategy接口:
定义了猜拳策略的抽象方法的接口,nextHand返回下一轮输出的手势,study是学习本轮手势。
public interface Strategy {public abstract Hand nextHand();public abstract void study(boolean win);}
WinningStrategy类:
Strategy接口的实现类之一。策略为如果上一轮的手势获胜了,则下一局则相同,如果上一轮输了,则下一局随机。
public class WinningStrategy implements Strategy{private Random random;private boolean won = false;private Hand prevHand;public WinningStrategy(int seed) {this.random = new Random(seed);}@Overridepublic Hand nextHand() {if(!won){prevHand = Hand.getHand(random.nextInt(3));}return prevHand;}@Overridepublic void study(boolean win) {won = win; // 根据输赢情况定义不同学习情况}}
ProbStrategy:
另外一种此策略,会根据之前的概率求出这轮应该出的手势。
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) {this.random = new Random(seed);}@Overridepublic Hand nextHand(){int bet=random.nextInt(getSum(currentHandValue));int handValue=0;if(bet<history[currentHandValue][0]){handValue = 0;}else if(bet<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;}@Overridepublic void study(boolean win) {if(win){history[prevHandValue][currentHandValue]++;}else {history[prevHandValue][(currentHandValue+1)%3]++;history[prevHandValue][(currentHandValue+2)%3]++;}}}
Player类:
猜拳选手实例对象。strategy属性存储策略,在Main中再生成具体策略,与player绑定,来实现策略设计模式。
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++;}@Overridepublic String toString() {return "Player{" +"name='" + name + '\'' +", strategy=" + strategy +", winCount=" + winCount +", loseCount=" + loseCount +", gameCount=" + gameCount +'}';}}
Main类:
public class Main {public static void main(String[] args) {if (args.length!=2) {System.out.println("输入格式错误");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("Hano", new ProbStrategy(seed2));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(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());}}}
Strategy中的登场角色:
Strategy(策略):
Strategy角色负责决定实现策略所必须的接口(API)。
ConcreteStrategy(具体的策略):
ConcreteStrategy角色负责实现Strategy角色的接口,即负责实现具体的策略(战略、方法和算法)
Context(上下文):
负责使用Strategy角色。Context角色保存了ConcreteStrategy的实例对象,并使用ConcreteStrategy角色去实现需求。
拓展:
为什么需要特意编写Strategy角色:
通常编程时算法会被写在具体方法中,Strategy模式却特意将算法与其他部分分离开来,只是定义了与算法相关的接口,然后在程序中以委托的方法来使用算法。
这样看起来程序好像变复杂了,其实不然。例如,当需要通过改善算法来提高算法的处理速度时,如果使用了Strategy模式,就不必修改Strategy角色的接口了,仅仅修改ConcreteStrategy角色即可。而且,使用委托这种弱关联关系可以很方便地整体替换算法。
程序运行中也可以切换策略:
如果使用Strategy模式,在程序运行中也可以切换ConcreteStrategy角色。此外,还可以用某种算法去“验证”另外一种算法。例如:假设要在某个表格计算软件的开发版本中进行复杂的计算。可以使用两种算法,首先时“高速但计算上可能有Bug的算法”和“低速但计算准确的算法”,然后用后者去验算前者的计算结果。
相关的设计模式:
Flyweight模式:
有时会使用Flyweight模式让多个地方可以共用ConcreteStrategy角色。
