zoukankan      html  css  js  c++  java
  • 一天一个设计模式——Strategy策略模式

    一、模式说明

      策略模式比较好理解,就是将程序中用到的算法整体的拿出来,并有多个不同版本的算法实现,在程序运行阶段,动态的决定使用哪个算法来解决问题。

      举个实际的例子:排序算法的问题,假如我们的程序中需要对数据进行排序,我们知道,不同的算法具有不同的时间复杂度和空间复杂度,因此需要在程序运行时,根据可用内存和数据特征,选用不同的算法(排序策略),这就是策略模式的使用场景之一。再举个例子,负载均衡算法:如果某个服务部署了多个冗余的实例,客户端在向服务端发送请求时,根据负载均衡算法策略,请求可能会被转发到不同的服务提供者实例来处理,如何决定某个请求转发给哪个服务实例呢,最简单的做法就是轮询,顺次将请求转发给每个服务实例进行处理。也可以采用随机方式,或者根据实际硬件环境和业务场景设置特定算法。

    二、模式类图

    三、程序示例

      在下面的演示策略模式的代码示例中,我们模拟猜拳游戏——剪刀石头布,猜拳的策略有两种:如果这次猜拳赢了,则下次还出同样的手势。另一种策略就是根据以前的猜拳结果,选择胜率最高的一种手势。

    1、Hand类:表示猜拳游戏中的手势,并非Strategy策略模式中的角色。

    package com.designpattern.cn.strategypattern;
    
    public class Hand {
        public static final int HANDVALUE_ROCK = 0; //表示石头
        public static final int HANDVALUE_SCISSORS = 1; //表示剪刀
        public static final int HANDVALUE_PEPER = 2; //表示布
        private static final Hand[] hand = {
                new Hand(HANDVALUE_ROCK),
                new Hand(HANDVALUE_SCISSORS),
                new Hand(HANDVALUE_PEPER)
        };
        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];
        }
    }
    View Code

    2、Strategy接口:

    package com.designpattern.cn.strategypattern;
    
    public interface Strategy {
        public abstract Hand nextHand();
        public abstract void study(boolean win);
    }
    View Code

    3、WinningStrategy类:

    package com.designpattern.cn.strategypattern;
    
    import java.util.Random;
    
    public 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;
        }
    }
    View Code

    4、ProbStrategy类:

    package com.designpattern.cn.strategypattern;
    
    import java.util.Random;
    
    public class ProbStrategy implements Strategy {
        private Random random;
        private int prevHandValue = 0;
        private int currentHandValue = 0;
        //history[上一局的手势][这一局的手势] 表达式的值越高表示过去的胜率越高
        //study方法会根据nextHand方法返回的手势胜负结果更新history字段中的值
        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][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 : history[hv]
                 ) {
                sum += i;
            }
            return sum;
        }
        public void study(boolean win){
            if(win){
                history[prevHandValue][currentHandValue]++;
            }else{
                history[prevHandValue][(currentHandValue+1)%3]++;
                history[prevHandValue][(currentHandValue+2)%3]++;
            }
        }
    }
    View Code

    5、Player类:

    package com.designpattern.cn.strategypattern;
    
    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" + "]";
        }
    }
    View Code

    6、Main类与运行结果:

    package com.designpattern.cn.strategypattern;
    
    import java.util.Random;
    
    public class Main {
        public static void main(String[] args){
            int seed1 = ((new Random()).nextInt(500))*(1+(new Random()).nextInt(500));
            int seed2 = seed1 * (new Random()).nextInt(500);
    
            Player player1 = new Player("Taro", new WinningStrategy(seed1));
            Player player2 = new Player("Hana", new ProbStrategy(seed2));
            for(int i = 0; i < 100000; 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);
                    player2.win();
                    player1.lose();
                }else{
                    System.out.println("Even...");
                    player1.even();
                    player2.even();
                }
            }
            System.out.println("Total result:");
            System.out.println(player1.toString());
            System.out.println(player2.toString());
        }
    }
    View Code

    四、Strategy策略模式中的角色

    • Strategy策略:负责定义实现策略必须的接口方法
    • ConcreteStrategy具体的策略:实现Strategy角色的接口,如程序中的WinningStrategy和ProbStrategy
    • Context上下文:负责使用Strategy策略,如示例程序中的player。

    五、相关的设计模式

    • Flyweight享元模式:通过使用享元模式,让多个地方共用ConcreteStrategy角色;
    • Abstract Factory抽象工厂:策略模式整体替换算法,抽象工厂整体替换具体的工厂,零件和产品;
    • State状态模式:状态模式和策略模式都可以替换被委托对象,而且类之间的关系也相似,只是两种模式目的不同。strategy策略模式替换被委托对象的类;状态模式中,每次状态发生变化时,被委托的对象必定会被替换。
  • 相关阅读:
    css3实现轮播2
    css3实现轮播1
    读阮一峰ES6笔记4:字符串的新增方法
    读阮一峰ES6笔记3:字符串的扩展
    应用流策略与检查配置结果
    配置流策略
    配置流行为
    配置流分类
    "流量监管"和"流量整形"的区别
    802.1p 优先级与内部优先级的映射关系
  • 原文地址:https://www.cnblogs.com/zheng-hong-bo/p/11109383.html
Copyright © 2011-2022 走看看