Patrón de diseño patrón de estrategia detallada

Autor: Liu Wenhui

El patrón de estrategia es un patrón de comportamiento ampliamente utilizado. La idea central es encapsular el algoritmo y delegarlo a diferentes objetos para su gestión. Este artículo se centrará en el patrón de estrategia para compartir.

I. Resumen

Si queremos lograr mantenibilidad y escalabilidad durante el desarrollo de software, necesitamos reutilizar el código tanto como sea posible y reducir el acoplamiento del código.El patrón de diseño es un método que puede mejorar la reutilización, mantenibilidad y escalabilidad del código.y soluciones para legibilidad.

Los 23 patrones de diseño bien conocidos se pueden dividir en tres categorías: patrones de creación, patrones estructurales y patrones de comportamiento. Entre ellos, el patrón de comportamiento puede usarse para describir cómo múltiples clases y múltiples objetos en el programa cooperan para completar tareas complejas, lo que implica la asignación de responsabilidades entre diferentes objetos y la abstracción de algoritmos. El patrón de estrategia es un patrón de comportamiento muy utilizado. Este artículo se centrará en el patrón de estrategia para aprender y compartir.

2. Conceptos básicos

La idea central del patrón de estrategia es encapsular el algoritmo y delegarlo a diferentes objetos para su gestión. De esta manera, podemos definir una serie de algoritmos, encapsular cada algoritmo en una serie de clases de estrategias concretas con una interfaz común, para que puedan ser reemplazadas de manera flexible y el algoritmo pueda cambiarse sin afectar al cliente. Al mismo tiempo, el modo de estrategia solo encapsula algoritmos (incluidos agregar y eliminar), pero no decide cuándo usar qué algoritmo, y la elección del algoritmo la determina el cliente.

Por ejemplo, hay muchas estrategias de viaje que podemos elegir cuando viajamos: bicicletas, automóviles, trenes y aviones. Cada estrategia de viaje tiene su propio método de uso. Siempre que podamos llegar al destino, podemos cambiar varias estrategias en voluntad. Otro ejemplo es cuando vamos al centro comercial, habrá muchas actividades promocionales en el centro comercial: descuento completo, reembolso, etc. Estos métodos de promoción son esencialmente algoritmos, y el algoritmo en sí también es una estrategia, que puede ser reemplazada en cualquier momento. Para el mismo producto, hoy Obtenga 50 de descuento cuando gaste 500 y obtenga 100 cupones cuando gaste 300 mañana Estas estrategias también son intercambiables.

Entonces, ¿cómo debemos usar el patrón de estrategia? Lo siguiente dará una introducción conceptual al patrón de estrategia desde dos niveles de estructura y pasos de uso.

2.1 Estructura

El patrón de estrategia contiene tres categorías, que son la categoría de estrategia abstracta, la categoría de estrategia concreta y la categoría de entorno. Son responsables de completar tareas específicas y están estrechamente relacionadas entre sí.

2.2 uso

Con los conceptos básicos anteriores, resumimos los pasos para usar el patrón de estrategia de la siguiente manera:

  • Paso 1: cree una clase de estrategia abstracta y defina una interfaz pública para la estrategia específica;

  • Paso 2: cree una clase de estrategia específica, que implemente la clase de estrategia abstracta a través de la interfaz y encapsule el algoritmo específico al mismo tiempo;

  • Paso 3: Cree una clase de entorno, mantenga una referencia a una clase de estrategia abstracta y proporciónela al cliente para que la llame.

3. Ejemplo de uso

Además del carnaval de compras Double 11, cada año se crean muchas otras actividades promocionales. Imagínese, si cada actividad promocional utiliza un modelo promocional, sería demasiado aburrido y hostil para los usuarios, comerciantes y plataformas. Por lo tanto, para mejorar la experiencia de compra del usuario y resaltar las características de marketing de los comerciantes, es necesario utilizar diferentes estrategias para diferentes actividades promocionales. Aquí tomamos la estrategia de promoción como ejemplo para analizar brevemente cómo se utiliza el modo de estrategia.

3.1 Implementación del código

//step1:定义抽象策略角色(Strategy):所有促销活动的共同接口
public interface Strategy {  
    void show();
}
​
//step2:定义具体策略角色(Concrete Strategy):不同类型的具体促销策略
//618大促活动 A
public class ConcreteStrategyA implements Strategy {
    @Override
    public void show() {
        System.out.println("618大促");
    }
}
​
//99大促活动 B
public class ConcreteStrategyB implements Strategy {
    @Override
    public void show() {
        System.out.println("99大促");
    }
}
​
//双11大促活动 C
public class ConcreteStrategyC implements Strategy {
    @Override
    public void show() {
        System.out.println("双11大促");
    }
}
​
//step3:定义环境角色(Context):把促销活动推送给用户,这里可理解为淘宝平台
public class Context{
    //持有抽象策略的引用
    private Strategy myStrategy;
    //生成构造方法,让平台根据传入的参数(type)选择促销活动
    public Context(Strategy strategyType) {
        this.myStrategy = strategyType;
    }
    //向用户展示促销活动
    public void taoPlatformShow(String time) {
        System.out.println(time + "的促销策略是:");
        myStrategy.show();
    }
}
​
//step4:客户端调用,需事先明确所有每种策略类如何使用
public class StrategyPattern{
  public static void main(String[] args){
        Context_TaoPlatform context;
    
        String time1 = "9月";
        Strategy strategyB = new ConcreteStrategyB();
        context = new Context(strategyB);
        context.taoPlatformShow(time1);
    
        String time2 = "11月";
        Strategy strategyC = new ConcreteStrategyC();
        context = new Context(strategyC);
        context.taoPlatformShow(time2);
    
        String time3 = "6月";
        Strategy strategyA = new ConcreteStrategyA();
        context = new Context(strategyA);
        context.taoPlatformShow(time3);
  }   
}

3.2 Salida de resultados

9月的促销策略是:
99大促
11月的促销策略是:
双11大促
6月的促销策略是:
618大促

3.3 diagramas UML

3.4 Diferencias con el patrón de fábrica simple

Como se puede ver en el ejemplo de código anterior y el diagrama de clase, el patrón de estrategia es muy similar al patrón de fábrica simple presentado en el artículo anterior.La principal diferencia entre los dos es la clase de contexto y la clase de fábrica. Para facilitar la comparación, echemos un vistazo a los códigos de estas dos clases por separado:

public class Context_TaoPlatform{
    //持有抽象策略的引用
    private Strategy myStrategy;
    //生成构造方法,让平台根据传入的参数(type)选择促销活动
    public TaoPlatform(Strategy strategyType) {
        this.myStrategy = strategyType;
    }
    //向用户展示促销活动
    public void taoPlatformShow(String time) {
        System.out.println(time + "的促销策略是:");
        myStrategy.show();
    }
}

public class Factory{
    public static Shirt exhibit(String ShirtName){
        switch(ShirtName){
            case "女款衬衫":
                return new WomenShirt();
            case "男款衬衫":
                return new MenShirt();
            default:
                return null;
        }
    }
}

Primero mire los parámetros de recepción: el método exhibi() en la clase Factory Factory recibe una cadena y devuelve un objeto Shirt; la clase de entorno Context_TaoPlatform necesita recibir un objeto Strategy cuando se inicializa. Es decir: la clase de fábrica crea un objeto correspondiente de acuerdo con las condiciones de recepción, y la clase Contexto recibe un objeto, y se pueden llamar métodos para ejecutar los métodos de este objeto.

Por ejemplo: Hay muchos tipos de bolígrafos, supongamos que hay una fábrica encargada de producir bolígrafos para diferentes propósitos.

Modo de fábrica: produzca bolígrafos para diferentes propósitos según el propósito dado por el usuario, como: si desea escribir caracteres de pincel, puede producir pinceles, y si desea escribir caracteres de bolígrafo, puede producir bolígrafos. Es decir, de acuerdo con un determinado atributo proporcionado por el usuario, se produce y devuelve al usuario un objeto que puede realizar los comportamientos correspondientes, y el foco está en qué tipo de objeto crear.

Modo de estrategia: use el lápiz producido por la fábrica para realizar el comportamiento correspondiente, como: escribir caracteres de pincel con un pincel y escribir caracteres de lápiz con un lápiz. Es decir, de acuerdo con un determinado objeto proporcionado por el usuario, se ejecuta el método correspondiente y el foco está en qué comportamiento elegir.

Cuatro, apreciación del código fuente de JDK

Aquí tomamos el comparador Comparator como ejemplo y comprendemos el modo de estrategia en profundidad analizando la implementación de su código fuente.

En JDK, cuando llamamos a un método de clasificación sort() de la clase de herramienta de matriz Arrays, podemos usar la regla de clasificación predeterminada (orden ascendente) o personalizar una regla de clasificación, es decir, personalizar la clasificación en orden ascendente o descendente. El código fuente es el siguiente:

public class Arrays{
    public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            //若没有传入Comparator接口的实现类对象,调用默认的升序排序方法
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                //jdk5及之前的传统归并排序,新版本中LegacyMergeSort.userRequested默认false
                legacyMergeSort(a, c);
            else
                //改进后的归并排序
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
}

En este punto, debemos pasar dos parámetros: uno es la matriz que se ordenará y el otro es el objeto de clase de implementación de la interfaz Comparator. Entre ellos, la interfaz Comparator es una interfaz funcional que define un método abstracto int compare (T o1, T o2), que se utiliza para definir una regla de clasificación específica. Aquí, la interfaz Comparator es la interfaz de estrategia abstracta en el patrón de estrategia, que define un algoritmo de clasificación, y la estrategia específica (el algoritmo de clasificación específico) será definida por el usuario, luego Arrays es una clase de entorno y el método sort() puede pasar en una estrategia c , permita que Arrays realice tareas de clasificación de acuerdo con esta estrategia.

public class demo {
    public static void main(String[] args) {
​
        Integer[] data = {12, 2, 3, 2, 4, 5, 1};
        // 实现降序排序
        Arrays.sort(data, new Comparator<Integer>() {
             // 排序策略 降序
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println(Arrays.toString(data)); //[12, 5, 4, 3, 2, 2, 1]
    }
}

En la clase de prueba anterior, cuando llamamos al método Arrays.sort(), el segundo parámetro es el objeto de clase de subimplementación de la interfaz Comparator. Se puede ver que el Comparador actúa como un rol de estrategia abstracta, mientras que la clase de sub-implementación específica actúa como un rol de estrategia concreto, y la clase de rol de entorno Arrays debe contener una referencia a la estrategia abstracta a llamar. Entonces, ¿el método Arrays.sort() usa el método compare() en la clase de subimplementación Comparator? Echemos un vistazo al método TimSort.sort(), el código fuente es el siguiente:

class TimSort<T> {
    static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                         T[] work, int workBase, int workLen) {
        assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
​
        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  // Arrays of size 0 and 1 are always sorted
​
        // If array is small, do a "mini-TimSort" with no merges
        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
            binarySort(a, lo, hi, lo + initRunLen, c);
            return;
        }
        ...
    }   
        
    private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,Comparator<? super T> c) {
        assert lo < hi;
        int runHi = lo + 1;
        if (runHi == hi)
            return 1;
​
        // Find end of run, and reverse range if descending
        if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
                runHi++;
            reverseRange(a, lo, runHi);
        } else {                              // Ascending
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
                runHi++;
        }
        return runHi - lo;
    }
}

El código anterior finalmente se ejecutará en el método countRunAndMakeAscending(), y se llama al método compare() cuando se ejecuta la declaración de juicio. Entonces, si solo usa el método compare(), solo necesita pasar el objeto de clase del método de anulación de compare() específico al llamar al método Arrays.sort().

5. Ventajas y desventajas y escenarios aplicables

5.1 Ventajas

  • Las clases de estrategia específicas se pueden cambiar libremente. Dado que todas las clases de estrategia específicas implementan la misma interfaz de estrategia abstracta, se pueden cambiar libremente.

  • Es compatible con el "principio de abrir y cerrar". Al agregar una nueva estrategia, solo necesita agregar una clase de estrategia específica y, básicamente, no necesita cambiar el código original.

  • Evite el uso de múltiples sentencias de selección condicional (en caso contrario), incorpore completamente la idea de diseño orientado a objetos.

5.2 Desventajas

  • El cliente debe conocer todas las clases de estrategias concretas, comprender la diferencia entre diferentes estrategias concretas y decidir qué clase de estrategia usar.

  • El patrón de estrategia generará muchas clases de estrategia específicas, lo que aumenta el número de clases en el sistema hasta cierto punto (el número de objetos se puede reducir hasta cierto punto utilizando el patrón de peso ligero).

5.3 Escenarios aplicables

  • Cuando un sistema necesita elegir dinámicamente uno de varios algoritmos, cada algoritmo se puede encapsular en una clase de estrategia específica.

  • Una clase define múltiples comportamientos, y estos comportamientos aparecen en forma de múltiples declaraciones condicionales en la operación de esta clase. Puede mover cada rama condicional a sus respectivas clases de estrategia para reemplazar estas declaraciones condicionales, y puede evitar el uso difícil de Mantener múltiples condicionales sentencias de selección, y encarnan los conceptos involucrados en la orientación a objetos.

  • Cada algoritmo en el sistema es completamente independiente entre sí, y los detalles de implementación del algoritmo específico deben ocultarse a los clientes para mejorar la confidencialidad y seguridad del algoritmo.

  • La única diferencia entre varias clases es que se comportan de manera diferente. Puede usar el modo de estrategia para seleccionar dinámicamente el comportamiento específico que se ejecutará en tiempo de ejecución.

6. Resumen

El patrón de estrategia es un patrón de diseño que es relativamente fácil de entender y usar. Solo encapsula algoritmos para facilitar la inserción de nuevos algoritmos en el sistema y el retiro de algoritmos antiguos del sistema. Este artículo mencionó en el análisis de las deficiencias del patrón de estrategia que el patrón de estrategia no determina cuándo usar qué algoritmo, y la selección del algoritmo la determina el cliente. Aunque esto mejora la flexibilidad del sistema hasta cierto punto, el el cliente necesita entender La distinción entre todas las clases de estrategias concretas, para elegir el algoritmo apropiado, aumenta la dificultad de uso por parte del cliente.

El patrón de estrategia y el patrón de fábrica tienen algunas similitudes en la estructura de su patrón, por lo que a veces es confuso. De hecho, hay muchas diferencias entre los dos: el modo fábrica es un modo creacional, su función es crear objetos, se enfoca en cómo crear objetos, y principalmente resuelve la distribución unificada de recursos, completamente independiente de la creación de objetos. , permitiendo La creación no tiene nada que ver con el cliente específico; el patrón de estrategia es un patrón de comportamiento, la función es dejar que un objeto elija un comportamiento entre muchos comportamientos, presta atención a cómo se encapsula el comportamiento y realiza el cambio flexible y expansión de la estrategia mediante la definición de la familia de estrategias, y Realice cambios en la política independientemente del cliente que utilice la política.

Además, en muchos escenarios, el modo de estrategia y el modo de fábrica se pueden usar en combinación para desempeñar un papel complementario. Por ejemplo, una de las desventajas del patrón de estrategia es que el usuario debe conocer todos los algoritmos de estrategia específicos, por lo que la estrategia específica inevitablemente será expuesta e inicializada por el módulo superior, lo cual es contrario a la ley de Dimit (el principio de mínimo conocimiento), mientras que el módulo superior y la capa inferior El desacoplamiento entre módulos se puede hacer por el modo de fábrica. Después de la combinación de los dos, el módulo superior no necesita conocer cada estrategia específica, siempre que el modo de estrategia se pueda realizar a través del Contexto.

Supongo que te gusta

Origin blog.csdn.net/AlibabaTech1024/article/details/130984749
Recomendado
Clasificación