[Patrones de diseño - Notas de estudio] 23 Patrones de diseño - Estrategia en modo estrategia (explicación del principio + introducción al escenario de la aplicación + introducción al caso + implementación del código Java)

Introducción del caso

  • Hay varios tipos de patos, como el pato salvaje, el pato pekinés, la cerceta, etc. Los patos tienen diversos comportamientos como caminar, graznar, volar, etc. Diferentes patos pueden comportarse de manera ligeramente diferente. Solicitud para mostrar información sobre patos.

Implementación de soluciones tradicionales

Diferentes patos heredan una clase padre Duck. Si tienen el mismo comportamiento, lo heredan. Si tienen comportamientos diferentes, anulan el método.

lograr

[Clase abstracta de pato]

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("鸭子会飞翔~~~");
   }

}

【Pato salvaje】

package com.atguigu.strategy;

public class WildDuck extends Duck {
    
    

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

}

【Pato Pekín】

package com.atguigu.strategy;

public class PekingDuck extends Duck {
    
    

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

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

}

【Pato de juguete】

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("玩具鸭不会飞翔~~~");
    }
}

analizar

  • Desventajas: debido a que heredan Duck, todos los patos tienen métodos de vuelo, aunque se puede solucionar anulando el método de mosca, si no se necesitan muchos métodos de la subclase, será muy problemático si hay que anular cada uno de ellos.
  • Mejora: utilizar patrón de estrategia

introducir

introducción básica

  • En el patrón de estrategia, las familias de algoritmos se definen y encapsulan en diferentes clases para que puedan reemplazarse entre sí. Este patrón realiza cambios en los algoritmos independientemente de los clientes que los utilizan. El uso del patrón de estrategia puede reemplazar completamente la parte de implementación del algoritmo, lo que nos permite resolver fácilmente el mismo problema con diferentes algoritmos.
  • Este patrón incorpora varios principios de diseño: separar el código cambiante del código no modificado; programar para interfaces en lugar de clases concretas (las interfaces estratégicas están definidas); usar más composición/agregación y menos herencia (los clientes lo usan a través de la composición)

Caracteres

Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí

El contexto agrega la interfaz de la estrategia. Si necesita utilizar un método de estrategia específico más adelante, simplemente pase la instancia de la estrategia específica como parámetro.

  • Strategy(策略): El rol de Estrategia es responsable de definir las interfaces (API) necesarias para implementar la estrategia.
  • ConcreteStrategy(具体的策略): El rol ConcreteStrategy es responsable de implementar la interfaz (API) del rol Estrategia, es decir, es responsable de implementar estrategias específicas (estrategia, dirección, métodos y algoritmos)
  • Context(上下文): Responsable de utilizar el rol de Estrategia. La función Contexto guarda una instancia de la función ConcreteStrategy y utiliza la función ConcreteStrategy (es decir, la interfaz que llama a la función Estrategia) para implementar los requisitos.

Implementación de casos

Caso numero uno

Diagrama de clase

Insertar descripción de la imagen aquí

lograr

【Comportamiento de mosca】

package com.atguigu.strategy.improve;

public interface FlyBehavior {
    
    

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

[Estrategia llamada QuackBehavior]

package com.atguigu.strategy.improve;

public interface QuackBehavior {
    
    
   void quack();
}

[Excelentes habilidades de vuelo: GoodFlyBehavior]

package com.atguigu.strategy.improve;

public class GoodFlyBehavior implements FlyBehavior {
    
    

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

}

[Habilidades de vuelo mediocres: BadFlyBehavior]

package com.atguigu.strategy.improve;

public class BadFlyBehavior implements FlyBehavior {
    
    

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

}

【No puedo volar】

package com.atguigu.strategy.improve;

public class NoFlyBehavior implements FlyBehavior{
    
    

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

}

[Clase abstracta de pato]

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



}

【Pato salvaje】

package com.atguigu.strategy.improve;

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


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

}

【Pato Pekín】

package com.atguigu.strategy.improve;

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

    }

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

}

【Pato de juguete】

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("玩具鸭不会游泳~~");
    }

}

【Categoria principal】

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

}

【Producción】

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

Process finished with exit code 0

La estrategia de gritar se implementa de la misma manera que la estrategia de volar, por lo que ya no se implementa aquí.

Caso 2

Diagrama de clase

Insertar descripción de la imagen aquí

lograr

[Tipo de gesto: no es un rol en el modo estrategia]

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

【Interfaz de estrategia】

package com.atguigu.strategy.Sample;

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

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

[Estrategia específica uno]

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

[Estrategia específica dos]

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

【Categoría de jugador】

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" + "]";
    }
}

【Categoria principal】

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

【correr】

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

Process finished with exit code 0

Preguntas y respuestas

Si necesita agregar una estrategia de gestos aleatorios, ¿cómo la implementa?

Respuesta: Simplemente use números aleatorios en el método nextHand, porque todos son aleatorios y no es necesario aprender de experiencias previas, por lo que el método de estudio puede ser un método vacío.

En el programa de muestra, el método de lucha de la clase Mano es responsable de determinar un empate. A la hora de emitir juicios la expresión que utiliza no es this.handValue == h.value, pero this==h, ¿por qué se puede escribir así?

Respuesta: Debido a que se utiliza el modo singleton, solo hay tres instancias de gesto. Si el valor de mano de los dos gestos es el mismo, significa que las dos instancias son la misma instancia.

Al escribir la clase WinningStrategy, la definición del campo ganado no se private boolean won = false; escribe de la siguiente manera private boolean won;: Aunque los métodos de escritura son diferentes, los resultados de ejecución de los dos son los mismos, ¿por qué?

Respuesta: Porque las variables globales se inicializarán automáticamente si no se les asigna un valor: el valor predeterminado del tipo booleano es falso; el valor predeterminado del tipo numérico es 0; el valor predeterminado del tipo de referencia es nulo. Tenga en cuenta que las variables locales no se inicializan automáticamente

El uso del patrón de estrategia en el código fuente JDK

En pocas palabras, puede especificar diferentes estrategias de clasificación al ordenar.

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

    }

}

Resumir

【ilustrar】

  • La clave del modelo estratégico es analizar las partes cambiantes y no cambiadas del proyecto.

【ventaja】

  • La idea central del patrón de estrategia es: usar más combinación/agregación y menos herencia; usar clases de comportamiento para combinación en lugar de herencia de comportamiento
  • Incorpora el principio de "cerrado a modificación y abierto a extensión". El cliente no necesita modificar el código original para agregar comportamiento, solo necesita agregar una estrategia (o comportamiento), evitando el uso de múltiples declaraciones de juicio (si ...de lo contrario si...de lo contrario)
  • Proporciona una forma de reemplazar la relación de herencia: el patrón de estrategia encapsula el algoritmo en una clase de estrategia independiente para que pueda cambiarlo independientemente de otros contextos, lo que facilita el cambio, la comprensión y la extensión.
  • La estrategia también se puede cambiar durante la ejecución del programa: si se utiliza el modo Estrategia, la función ConcreteStrategy también se puede cambiar durante la ejecución del programa. Por ejemplo, un algoritmo se usa en un entorno con menos memoria y otro algoritmo se usa en un entorno con más memoria.

【defecto】

  • Cada vez que se agrega una estrategia, se agregará una clase. Si hay demasiadas estrategias, la cantidad de clases será enorme.

【Preguntas y respuestas】

Por qué es necesario escribir roles estratégicos especiales

Respuesta: Cuando queremos aumentar la velocidad de procesamiento del algoritmo mejorando el algoritmo, si se usa el modo Estrategia, no es necesario modificar la interfaz (API) de la función Estrategia, solo la función ConcreteStrategy. Además, el uso de una relación débil como la delegación puede reemplazar fácilmente el algoritmo en su conjunto, lo que facilita la comparación de algoritmos.

Descripción del articulo

  • Este artículo son mis notas de estudio para estudiar Shang Silicon Valley. La mayor parte del contenido del artículo proviene de videos de Shang Silicon Valley (haga clic para conocer los cursos relacionados con Shang Silicon Valley ), y parte del contenido proviene de mi propio pensamiento. artículo para ayudar a otras personas a estudiar más cómodamente. Organice sus propias notas o aprenda conocimientos relevantes directamente a través de artículos. Si hay alguna infracción, comuníquese con nosotros para eliminarla. Finalmente, me gustaría expresar mi gratitud a Shang Silicon Valley por su cursos de alta calidad.
  • También leí simultáneamente el libro "Patrón de diseño gráfico" (Patrón de diseño gráfico/(japonés) Hiroshi Yuki; traducido por Yang Wenxuan - Beijing: People's Posts and Telecommunications Press, 2017.1), y luego integré los contenidos de los dos para hacer el conocimiento. puntos más completos.

Supongo que te gusta

Origin blog.csdn.net/laodanqiu/article/details/132291516
Recomendado
Clasificación