[Padrões de Design - Notas de Estudo] 23 Padrões de Design - Estratégia do Modo Estratégia (explicação do princípio + introdução ao cenário do aplicativo + introdução ao caso + implementação do código Java)

Introdução de caso

  • Existem vários tipos de patos, como pato selvagem, pato laqueado, azul-petróleo, etc. Os patos têm vários comportamentos, como caminhar, grasnar, voar, etc. Patos diferentes podem se comportar de maneira um pouco diferente. Solicitação para exibir informações do pato

Implementação de solução tradicional

Patos diferentes herdam uma classe pai Duck. Se tiverem o mesmo comportamento, eles o herdam. Se tiverem comportamentos diferentes, eles substituem o método.

concluir

[Aula abstrata 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 selvagem】

package com.atguigu.strategy;

public class WildDuck extends Duck {
    
    

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

}

【Pato de Pequim】

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 brinquedo】

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

analisar

  • Desvantagens: Por herdarem Duck, todos os patos têm métodos de vôo. Embora possa ser resolvido substituindo o método fly, se muitos métodos da subclasse não forem necessários, será muito problemático se cada um tiver que ser substituído.
  • Melhoria: Use Padrão de Estratégia

introduzir

introdução básica

  • No padrão de estratégia, as famílias de algoritmos são definidas e encapsuladas em diferentes classes para que possam ser substituídas entre si.Esse padrão torna as alterações de algoritmos independentes dos clientes que usam os algoritmos. O uso do padrão de estratégia pode substituir completamente a parte de implementação do algoritmo, permitindo-nos resolver facilmente o mesmo problema com algoritmos diferentes.
  • Este padrão incorpora vários princípios de design: separar o código mutável do código inalterado; programar para interfaces em vez de classes concretas (interfaces estratégicas são definidas); usar mais composição/agregação e menos herança (os clientes usam isso por meio de composição) Estratégia)

Personagens

Insira a descrição da imagem aqui

Insira a descrição da imagem aqui

O contexto agrega a interface da estratégia. Se você precisar usar um método de estratégia específico posteriormente, basta passar a instância da estratégia específica como parâmetro.

  • Strategy(策略): A função Estratégia é responsável por definir as interfaces (APIs) necessárias para implementar a estratégia
  • ConcreteStrategy(具体的策略): O papel ConcreteStrategy é responsável por implementar a interface (API) do papel Estratégia, ou seja, é responsável por implementar estratégias específicas (estratégia, direção, métodos e algoritmos)
  • Context(上下文): Responsável por usar a função de Estratégia. A função Context salva uma instância da função ConcreteStrategy e usa a função ConcreteStrategy (ou seja, a interface que chama a função Strategy) para implementar os requisitos.

Implementação de caso

Caso número um

Diagrama de classes

Insira a descrição da imagem aqui

concluir

【Comportamento de mosca】

package com.atguigu.strategy.improve;

public interface FlyBehavior {
    
    

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

[Chamada estratégia QuackBehavior]

package com.atguigu.strategy.improve;

public interface QuackBehavior {
    
    
   void quack();
}

[Excelentes habilidades de vôo: GoodFlyBehavior]

package com.atguigu.strategy.improve;

public class GoodFlyBehavior implements FlyBehavior {
    
    

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

}

[Habilidades de vôo medíocres: BadFlyBehavior]

package com.atguigu.strategy.improve;

public class BadFlyBehavior implements FlyBehavior {
    
    

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

}

【Não posso voar】

package com.atguigu.strategy.improve;

public class NoFlyBehavior implements FlyBehavior{
    
    

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

}

[Aula abstrata 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 selvagem】

package com.atguigu.strategy.improve;

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


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

}

【Pato de Pequim】

package com.atguigu.strategy.improve;

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

    }

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

}

【Pato de brinquedo】

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

}

【Saída】

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

Process finished with exit code 0

A estratégia de gritar é implementada da mesma forma que a estratégia de voar, por isso não é mais implementada aqui.

Caso 2

Diagrama de classes

Insira a descrição da imagem aqui

concluir

[Tipo de gesto: não é uma função no modo de estratégia]

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

【Interface estratégica】

package com.atguigu.strategy.Sample;

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

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

[Estratégia específica um]

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

[Estratégia específica dois]

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

【Categoria de jogador】

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

Perguntas e respostas

Se você precisar adicionar uma estratégia de gestos aleatórios, como implementá-la?

Resposta: Basta usar números aleatórios no método nextHand, porque eles são todos aleatórios e não há necessidade de aprender com experiências anteriores, portanto o método de estudo pode ser um método vazio.

No programa exemplo, o método de luta da classe Hand é responsável por determinar o empate. Ao fazer julgamentos, a expressão que utiliza não é this.handValue == h.value, mas this==h, por que pode ser escrita assim?

Resposta: Como o modo singleton é usado, existem apenas três instâncias de gesto. Se o handValue dos dois gestos for o mesmo, significa que as duas instâncias são a mesma instância.

Ao escrever a classe WinningStrategy, a definição do campo ganho não é private boolean won = false; escrita da seguinte forma private boolean won;. Embora os métodos de escrita sejam diferentes, os resultados de execução dos dois são os mesmos. Por quê?

Resposta: Como as variáveis ​​globais serão inicializadas automaticamente se não receberem um valor: o valor padrão do tipo booleano é falso; o valor padrão do tipo numérico é 0; o valor padrão do tipo de referência é nulo. Observe que variáveis ​​locais não são inicializadas automaticamente

O uso do padrão de estratégia no código-fonte JDK

Simplificando, você pode especificar diferentes estratégias de classificação ao classificar.

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】

  • A chave para o modelo de estratégia é analisar as partes mutáveis ​​e inalteradas do projeto

【vantagem】

  • A ideia central do padrão de estratégia é: usar mais combinação/agregação e menos herança; usar classes de comportamento para combinação em vez de herança de comportamento
  • Ele incorpora o princípio de "fechado à modificação e aberto à extensão". O cliente não precisa modificar o código original para adicionar comportamentos. Ele só precisa adicionar uma estratégia (ou comportamento), evitando o uso de múltiplas declarações de julgamento (se ... senão se... senão)
  • Fornece uma maneira de substituir o relacionamento de herança: o padrão de estratégia encapsula o algoritmo em uma classe de estratégia independente para que você possa alterá-lo independentemente de outros contextos, tornando-o fácil de alternar, fácil de entender e fácil de estender
  • A estratégia também pode ser trocada durante a execução do programa: Se o modo Estratégia for utilizado, a função ConcreteStrategy também pode ser trocada durante a execução do programa. Por exemplo, um algoritmo é usado em um ambiente com menos memória e outro algoritmo é usado em um ambiente com mais memória.

【deficiência】

  • Cada vez que uma estratégia for adicionada, uma classe será adicionada. Se houver muitas estratégias, o número de classes será enorme.

【Perguntas e Respostas】

Por que você precisa escrever especialmente funções de estratégia

Resposta: Quando queremos aumentar a velocidade de processamento do algoritmo melhorando o algoritmo, se for utilizado o modo Estratégia, não há necessidade de modificar a interface (API) da função Estratégia, apenas a função ConcreteStrategy. Além disso, o uso de um relacionamento fraco, como a delegação, pode facilmente substituir o algoritmo como um todo, o que facilita a comparação de algoritmos.

Descrição do artigo

  • Este artigo é minhas notas de estudo para estudar Shang Silicon Valley. A maior parte do conteúdo do artigo vem de vídeos de Shang Silicon Valley ( clique para aprender cursos relacionados a Shang Silicon Valley ) e parte do conteúdo vem de meu próprio pensamento. Eu publico este artigo para ajudar outras pessoas que estudam de forma mais conveniente. Organize suas próprias anotações ou aprenda conhecimentos relevantes diretamente por meio de artigos. Se houver alguma violação, entre em contato conosco para excluí-la. Por fim, gostaria de expressar minha gratidão a Shang Silicon Valley por sua cursos de alta qualidade.
  • Também li simultaneamente o livro "Graphic Design Pattern" (Graphic Design Pattern/(japonês) Hiroshi Yuki; traduzido por Yang Wenxuan - Pequim: People's Posts and Telecommunications Press, 2017.1), e depois integrei o conteúdo dos dois para tornar o conhecimento pontos mais abrangentes.

Acho que você gosta

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