该文章是阅读《图解设计模式》的学习笔记。书本链接https://www.ituring.com.cn/book/1811
Strategy模式的作用是可以整体地替换算法的实现部分,让编程者轻松地以不同算法解决同一个问题。下面以猜拳游戏分两种猜拳策略为例,来说明Strategy模式,先来看类结构图:
Hand类代码(用于标识猜拳游戏中的手势):
package com.wen.Strategy;
public class Hand {
public static final int SHITOU = 0;
public static final int JIANDAO = 1;
public static final int BU = 2;
public static final Hand[] hand = {
new Hand(SHITOU),
new Hand(JIANDAO),
new Hand(BU),
};
private static final String[] name ={
"石头","剪刀","布",
};
private int handValue;
public Hand(int handValue) {
this.handValue = handValue;
}
//根据手势获取对应的Hand实例
public static Hand getHand(int handValue) {
return hand[handValue];
}
private int fight(Hand hand){//计分方法,平局0分,赢了1分,输了-1分
if(this == hand){
return 0;
//当前的值加1取余3,如当前值是布,布+1(即2+1)取余3是石头,布赢石头;其它情况同理
}else if((this.handValue +1) % 3 == hand.handValue){
return 1;
}else{
return -1;
}
}
public boolean isStrongerThan(Hand hand){
return fight(hand) == 1;
}
public boolean isWeakerThan(Hand hand){
return fight(hand) == -1;
}
@Override
public String toString() {
return "Hand{" +
"handValue=" + handValue +
'}';
}
}
Strategy类代码:
package com.wen.Strategy;
public interface Strategy {
public abstract Hand nextHand();
public abstract void study(boolean win);
}
ProbStrategy类代码:
package com.wen.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 range){
random = new Random(range);
}
@Override
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);
}
@Override
public void study(boolean win) {
if(win){
history[prevHandValue][currentHandValue]++;
}else{
history[prevHandValue][(currentHandValue+1)%3]++;
history[prevHandValue][(currentHandValue+2)%3]++;
}
}
private int getSum(int handValue){
int sum = 0;
for(int i=0;i<3;i++){
sum+=history[handValue][i];
}
return sum;
}
}
WinningStrategy类代码:
package com.wen.Strategy;
import java.util.Random;
public class WinningStrategy implements Strategy {
private Random random;
private boolean win = false;
private Hand nextHand;
public WinningStrategy(int range){
random = new Random(range);
}
@Override
public Hand nextHand() {
if(!win){
nextHand = Hand.getHand(random.nextInt(3));
}
return nextHand;
}
@Override
public void study(boolean win) {
this.win = win;
}
}
Player类代码:
package com.wen.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++;
}
@Override
public String toString() {
return "Player{" +
"name='" + name + '\'' +
", gamecount=" + gamecount +
", wincount=" + wincount +
", losecount=" + losecount +
'}';
}
}
Main入口类代码:
package com.wen.Strategy;
public class Main {
public static void main(String[] args) {
int seed1 = 123;
int seed2 = 456;
Player player1 = new Player("小明", new WinningStrategy(seed1));
Player player2 = new Player("小红", 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("胜利者:" + player1);
player1.win();
player2.lose();
} else if (nextHand2.isStrongerThan(nextHand1)) {
System.out.println("胜利者:" + player2);
player1.lose();
player2.win();
} else {
System.out.println("平局...");
player1.even();
player2.even();
}
}
System.out.println("最终结果:");
System.out.println(player1.toString());
System.out.println(player2.toString());
}
}
程序运行结果:
分析该模式中的角色:
Strategy(策略) :负责定义实现策略所必需的方法接口。
ConcreteStrategy(具体的策略):负责实现策略角色中定义的接口方法。
Context(上下文):该角色负责使用Strategy角色里面定义了接口方法,并保存了ConcreteStrategy(具体的策略)角色的实例去使用具体的需求。
可扩展性:
编写了Strategy角色,在需要替换算法策略时,只需要修改ConcreteStrategy角色即可,在Player类中使用调用Strategy类实例这种委托的弱关联关系可以更方便地整体替换策略算法。如:编写棋牌类游戏程序时,使用Strategy模式可以更方便地根据选择切换AI的难度。