[Design Patterns - Study Notes] 23 Design Patterns - Strategy Mode Strategy (principle explanation + application scenario introduction + case introduction + Java code implementation)

Case introduction

  • There are various kinds of ducks, such as wild duck, Peking duck, teal, etc. Ducks have various behaviors such as walking, quacking, flying, etc. Different ducks may behave slightly differently. Request to display duck information

Traditional solution implementation

Different ducks inherit a parent class Duck. If they have the same behavior, they inherit it. If they have different behaviors, they override the method.

accomplish

[Duck abstract class]

package com.atguigu.strategy;

public abstract class Duck {
    
    

   public Duck() {
    
    

   }

   /**
    * 显示鸭子信息
    */
   public abstract void display();

   /**
    * 叫法
    */
   public void quack() {
    
    
      System.out.println("鸭子嘎嘎叫~~");
   }

   /**
    * 游泳方法
    */
   public void swim() {
    
    
      System.out.println("鸭子会游泳~~");
   }

   /**
    * 飞翔方法
    */
   public void fly() {
    
    
      System.out.println("鸭子会飞翔~~~");
   }

}

【wild duck】

package com.atguigu.strategy;

public class WildDuck extends Duck {
    
    

   @Override
   public void display() {
    
    
      System.out.println(" 这是野鸭 ");
   }

}

【Peking Duck】

package com.atguigu.strategy;

public class PekingDuck extends Duck {
    
    

   @Override
   public void display() {
    
    
      System.out.println("~~北京鸭~~~");
   }

   /**
    * 因为北京鸭不能飞翔,因此需要重写fly
    */
   @Override
   public void fly() {
    
    
      System.out.println("北京鸭不能飞翔");
   }

}

【Toy Duck】

package com.atguigu.strategy;

public class ToyDuck extends Duck {
    
    
    @Override
    public void display() {
    
    
        System.out.println("玩具鸭");
    }

    //-------需要重写父类的所有方法---------

    public void quack() {
    
    
        System.out.println("玩具鸭不能叫~~");
    }

    public void swim() {
    
    
        System.out.println("玩具鸭不会游泳~~");
    }

    public void fly() {
    
    
        System.out.println("玩具鸭不会飞翔~~~");
    }
}

analyze

  • Disadvantages: Because they inherit Duck, all ducks have flying methods. Although it can be solved by overriding the fly method, if many methods of the subclass are not needed, it will be very troublesome if each one has to be overridden.
  • Improvement: Use Strategy Pattern

introduce

basic introduction

  • In the strategy pattern, algorithm families are defined and encapsulated into different classes so that they can be replaced with each other. This pattern makes algorithm changes independent of the customers who use the algorithms. Using the strategy pattern can completely replace the implementation part of the algorithm, allowing us to easily solve the same problem with different algorithms.
  • This pattern embodies several design principles: separate changing code from unchanged code; program for interfaces rather than concrete classes (strategic interfaces are defined); use more combination/aggregation and less inheritance (customers use it through combination Strategy)

Characters

Insert image description here

Insert image description here

Context aggregates the strategy interface. If you need to use a specific strategy method later, just pass in the instance of the specific strategy as a parameter.

  • Strategy(策略): The Strategy role is responsible for defining the interfaces (APIs) necessary to implement the strategy
  • ConcreteStrategy(具体的策略): The ConcreteStrategy role is responsible for implementing the interface (API) of the Strategy role, that is, it is responsible for implementing specific strategies (strategy, direction, methods and algorithms)
  • Context(上下文): Responsible for using the Strategy role. The Context role saves an instance of the ConcreteStrategy role and uses the ConcreteStrategy role (that is, the interface that calls the Strategy role) to implement the requirements.

Case implementation

Case number one

Class Diagram

Insert image description here

accomplish

【FlyBehavior】

package com.atguigu.strategy.improve;

public interface FlyBehavior {
    
    

   /**
    * 让子类具体实现
    */
   void fly();
}

[Called strategy QuackBehavior]

package com.atguigu.strategy.improve;

public interface QuackBehavior {
    
    
   void quack();
}

[Superb flying skills: GoodFlyBehavior]

package com.atguigu.strategy.improve;

public class GoodFlyBehavior implements FlyBehavior {
    
    

   @Override
   public void fly() {
    
    
      System.out.println(" 飞翔技术高超 ~~~");
   }

}

[Mediocre flying skills: BadFlyBehavior]

package com.atguigu.strategy.improve;

public class BadFlyBehavior implements FlyBehavior {
    
    

   @Override
   public void fly() {
    
    
      System.out.println(" 飞翔技术一般 ");
   }

}

【Can't fly】

package com.atguigu.strategy.improve;

public class NoFlyBehavior implements FlyBehavior{
    
    

   @Override
   public void fly() {
    
    
      System.out.println(" 不会飞翔  ");
   }

}

[Duck abstract class]

package com.atguigu.strategy.improve;

public abstract class Duck {
    
    

   /**
    * 策略接口 飞翔
    */
   FlyBehavior flyBehavior;
   /**
    * 策略接口 叫
    */
   QuackBehavior quackBehavior;

   public Duck() {
    
    

   }

   /**
    * 显示鸭子信息
    */
   public abstract void display();

   public void quack() {
    
    
      System.out.println("鸭子嘎嘎叫~~");
   }

   public void swim() {
    
    
      System.out.println("鸭子会游泳~~");
   }

   public void fly() {
    
    
      //改进
      if(flyBehavior != null) {
    
    
         flyBehavior.fly();
      }
   }

   public void setFlyBehavior(FlyBehavior flyBehavior) {
    
    
      this.flyBehavior = flyBehavior;
   }


   public void setQuackBehavior(QuackBehavior quackBehavior) {
    
    
      this.quackBehavior = quackBehavior;
   }



}

【wild duck】

package com.atguigu.strategy.improve;

public class WildDuck extends Duck {
    
    
   
   /**
    * 构造器,传入FlyBehavor 的对象
    */
   public  WildDuck() {
    
    
      // 野鸭飞翔技术较强
      flyBehavior = new GoodFlyBehavior();
   }


   @Override
   public void display() {
    
    
      System.out.println(" 这是野鸭 ");
   }

}

【Peking Duck】

package com.atguigu.strategy.improve;

public class PekingDuck extends Duck {
    
    
    
    /**
     * 假如北京鸭可以飞翔,但是飞翔技术一般
     */
    public PekingDuck() {
    
    
        flyBehavior = new BadFlyBehavior();

    }

    @Override
    public void display() {
    
    
        System.out.println("~~北京鸭~~~");
    }

}

【Toy Duck】

package com.atguigu.strategy.improve;

public class ToyDuck extends Duck {
    
    
    public ToyDuck() {
    
    
        // 玩具鸭不会飞翔
        flyBehavior = new NoFlyBehavior();
    }

    @Override
    public void display() {
    
    
        System.out.println("玩具鸭");
    }

    /**
     * 需要重写父类的所有方法
     */
    public void quack() {
    
    
        System.out.println("玩具鸭不能叫~~");
    }

    public void swim() {
    
    
        System.out.println("玩具鸭不会游泳~~");
    }

}

【Main category】

package com.atguigu.strategy.improve;

public class Client {
    
    

   public static void main(String[] args) {
    
    
      WildDuck wildDuck = new WildDuck();
      wildDuck.fly();

      ToyDuck toyDuck = new ToyDuck();
      toyDuck.fly();

      PekingDuck pekingDuck = new PekingDuck();
      pekingDuck.fly();
      
      //可以动态改变某个对象的行为, 将北京鸭改成不能飞
      pekingDuck.setFlyBehavior(new NoFlyBehavior());
      System.out.println("北京鸭的实际飞翔能力");
      pekingDuck.fly();
   }

}

【Output】

飞翔技术高超 ~~~
 不会飞翔  
 飞翔技术一般 
北京鸭的实际飞翔能力
 不会飞翔  

Process finished with exit code 0

The screaming strategy is implemented in the same way as the flying strategy, so it is no longer implemented here.

Case 2

Class Diagram

Insert image description here

accomplish

[Gesture type: not a role in strategy mode]

package com.atguigu.strategy.Sample;

/**
 * 手势
 */
public class Hand {
    
    
    /**
     * 表示石头的值
     */
    public static final int HANDVALUE_GUU = 0;
    /**
     * 表示剪刀的值
     */
    public static final int HANDVALUE_CHO = 1;
    /**
     * 表示布的值
     */
    public static final int HANDVALUE_PAA = 2;
    /**
     * 表示猜拳中3种手势的实例
     */
    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;
    }

    /**
     * 根据手势的值获取其对应的实例,这是一种单例模式,每种手势只有一个实例
     *
     * @param handvalue
     * @return
     */
    public static Hand getHand(int handvalue) {
    
    
        return hand[handvalue];
    }

    /**
     * 如果this胜了h则返回true
     *
     * @param h
     * @return
     */
    public boolean isStrongerThan(Hand h) {
    
    
        return fight(h) == 1;
    }

    /**
     * 如果this输给了h则返回true
     *
     * @param h
     * @return
     */
    public boolean isWeakerThan(Hand h) {
    
    
        return fight(h) == -1;
    }

    /**
     * 计分:平0, 胜1, 负-1
     *
     * @param h
     * @return
     */
    private int fight(Hand h) {
    
    
        if (this == h) {
    
    
            return 0;
        } else if ((this.handvalue + 1) % 3 == h.handvalue) {
    
    
            // 当(this.handvalue + 1) % 3 == h.handvalue时,可能得手势组合如下
            // this是石头,h是剪刀
            // this是剪刀,h是布
            // this是布,h是石头
            return 1;
        } else {
    
    
            return -1;
        }
    }

    /**
     * 转换为手势值所对应的字符串
     *
     * @return
     */
    public String toString() {
    
    
        return name[handvalue];
    }
}

【Strategy interface】

package com.atguigu.strategy.Sample;

public interface Strategy {
    
    
    /**
     * 获取下一局要出的手势
     * @return
     */
    public abstract Hand nextHand();

    /**
     * 学习上一局的手势是否获胜了,获胜就传进来true,否则返回false
     * @param win
     */
    public abstract void study(boolean win);
}

[Specific strategy one]

package com.atguigu.strategy.Sample;

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

[Specific strategy two]

package com.atguigu.strategy.Sample;

import java.util.Random;

public class ProbStrategy implements Strategy {
    
    
    private Random random;
    private int prevHandValue = 0;
    private int currentHandValue = 0;
    /**
     * 过去的胜率:history[上一局出的手势][这一局所出的手势]
     * 假设上一局出的手势是石头:
     * history[0][0]:两局分别出了石头、石头的获胜次数
     * history[0][1]:两局分别出了石头、剪刀的获胜次数
     * history[0][2]:两局分别出了石头、布的获胜次数
     * 若history[0][0]=3;history[0][1]=5;history[0][2]=7
     * 下一把出什么?使用轮盘赌的方式,出石头的概率是3/15;出剪刀的概率是5/15;出布的概率是7/15
     */
    private int[][] history = {
    
    
        {
    
     1, 1, 1, },
        {
    
     1, 1, 1, },
        {
    
     1, 1, 1, },
    };
    public ProbStrategy(int seed) {
    
    
        random = new Random(seed);
    }

    /**
     * 学习历史胜率,根据轮盘赌的方式来出下一个手势
     * @return
     */
    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);
    }

    /**
     * 获取第一把出hv,第二把出1、2、3的总次数
     * @param hv
     * @return
     */
    private int getSum(int hv) {
    
    
        int sum = 0;
        for (int i = 0; i < 3; i++) {
    
    
            sum += history[hv][i];
        }
        return sum;
    }

    /**
     * 学习经验,更新 history 表格
     * @param win
     */
    public void study(boolean win) {
    
    
 
        if (win) {
    
    
            history[prevHandValue][currentHandValue]++;
        } else {
    
    
            history[prevHandValue][(currentHandValue + 1) % 3]++;
            history[prevHandValue][(currentHandValue + 2) % 3]++;
        }
    }
}

【Gamer Category】

package com.atguigu.strategy.Sample;

/**
 * 玩猜拳游戏的选手类
 */
public class Player {
    
    
    private String name;
    /**
     * 记录选手要选用的策略
     */
    private Strategy strategy;

    /**
     * 赢的局数
     */
    private int wincount;
    /**
     * 输的局数
     */
    private int losecount;
    /**
     * 总局数
     */
    private int gamecount;

    /**
     * 传入选手的姓名和策略
     *
     * @param name
     * @param strategy
     */
    public Player(String name, Strategy strategy) {
    
    
        this.name = name;
        this.strategy = strategy;
    }

    /**
     * 策略决定下一局要出的手势
     *
     * @return
     */
    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 category】

package com.atguigu.strategy.Sample;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        // 让选手分别使用两种策略来比试
        Player player1 = new Player("Taro", new WinningStrategy(314));
        Player player2 = new Player("Hana", new ProbStrategy(12));
        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());
    }
}

【run】

Total result:
[Taro:10000 games, 3107 win, 3617 lose]
[Hana:10000 games, 3617 win, 3107 lose]

Process finished with exit code 0

Q&A

If you need to add a random gesture strategy, how do you implement it?

Answer: Just use random numbers in the nextHand method, because they are all random and there is no need to learn from previous experience, so the study method can be an empty method.

In the sample program, the fight method of the Hand class is responsible for determining a tie. When making judgments, the expression it uses is not this.handValue == h.value, but this==h, why can it be written like this?

Answer: Because the singleton mode is used, there are only three gesture instances. If the handValue of the two gestures is the same, it means that the two instances are the same instance.

When writing the WinningStrategy class, the definition of the won field is not private boolean won = false; written as follows private boolean won;. Although the writing methods are different, the running results of the two are the same. Why?

Answer: Because global variables will be automatically initialized if they are not assigned a value: the default value of boolean type is false; the default value of numeric type is 0; the default value of reference type is null. Note that local variables are not automatically initialized

The use of strategy pattern in JDK source code

To put it simply, you can specify different sorting strategies when sorting.

package com.atguigu.jdk;

import java.util.Arrays;
import java.util.Comparator;


public class Strategy {
    
    

    public static void main(String[] args) {
    
    
        // TODO Auto-generated method stub
        //数组
        Integer[] data = {
    
    9, 1, 2, 8, 4, 3};
        // 实现降序排序,返回-1放左边,1放右边,0保持不变

        // 说明
        // 1. 实现了 Comparator 接口(策略接口) , 匿名类对象:new Comparator<Integer>(){..}
        // 2. 对象 new Comparator<Integer>(){..} 就是实现了 策略接口 的对象
        // 3. public int compare(Integer o1, Integer o2){} 指定具体的处理策略
        Comparator<Integer> comparator = new Comparator<Integer>() {
    
    
            public int compare(Integer o1, Integer o2) {
    
    
                if (o1 > o2) {
    
    
                    return -1;
                } else {
    
    
                    return 1;
                }
            }
        };

        // sort源码说明 传入数字和一个排序策略
      /*
       * public static <T> void sort(T[] a, Comparator<? super T> c) {
              if (c == null) {
                  sort(a); //默认方法
              } else {
                  if (LegacyMergeSort.userRequested)
                      legacyMergeSort(a, c); //使用策略对象c
                  else
                     // 使用策略对象c
                      TimSort.sort(a, 0, a.length, c, null, 0, 0);
              }
          }
       */
        // 方式1
        Arrays.sort(data, comparator);
      // 降序排序
        System.out.println(Arrays.toString(data));

        //方式2- 同时lambda 表达式实现 策略模式
        Integer[] data2 = {
    
    19, 11, 12, 18, 14, 13};

        // 换一个排序策略
        Arrays.sort(data2, (var1, var2) -> {
    
    
            if (var1.compareTo(var2) > 0) {
    
    
                return -1;
            } else {
    
    
                return 1;
            }
        });

        System.out.println("data2=" + Arrays.toString(data2));

    }

}

Summarize

【illustrate】

  • The key to the strategy model is to analyze the changing and unchanged parts of the project

【advantage】

  • The core idea of ​​the strategy pattern is: use combination/aggregation more and inheritance less; use behavior classes for combination instead of inheritance of behavior
  • It embodies the principle of "closed to modification and open to extension". The client does not need to modify the original code to add behaviors. It only needs to add a strategy (or behavior), avoiding the use of multiple judgment statements (if...else if...else)
  • Provides a way to replace the inheritance relationship: Strategy pattern encapsulates the algorithm in an independent Strategy class so that you can change it independently of other Contexts, making it easy to switch, easy to understand, and easy to extend
  • The strategy can also be switched during the running of the program: If the Strategy mode is used, the ConcreteStrategy role can also be switched during the running of the program. For example, one algorithm is used in an environment with less memory, and another algorithm is used in an environment with more memory.

【shortcoming】

  • Every time a strategy is added, a class will be added. If there are too many strategies, the number of classes will be huge.

【Q&A】

Why you need to specially write Strategy roles

Answer: When we want to increase the processing speed of the algorithm by improving the algorithm, if the Strategy mode is used, there is no need to modify the interface (API) of the Strategy role, only the ConcreteStrategy role. Moreover, using a weak relationship such as delegation can easily replace the algorithm as a whole, which makes it easier to compare algorithms.

Article description

  • This article is my study notes for studying Shang Silicon Valley. Most of the content in the article comes from Shang Silicon Valley videos ( click to learn Shang Silicon Valley related courses ), and some of the content comes from my own thinking. I publish this article to help other people who study more conveniently. Organize your own notes or learn relevant knowledge directly through articles. If there is any infringement, please contact us to delete it. Finally, I would like to express my gratitude to Shang Silicon Valley for its high-quality courses.
  • I also simultaneously read the book "Graphic Design Pattern" (Graphic Design Pattern/(Japanese) Hiroshi Yuki; translated by Yang Wenxuan - Beijing: People's Posts and Telecommunications Press, 2017.1), and then integrated the contents of the two to make the knowledge points more comprehensive.

Guess you like

Origin blog.csdn.net/laodanqiu/article/details/132291516