Ejemplo del mundo real del patrón de estrategia


94

He estado leyendo sobre el director de OCP y cómo usar el patrón de estrategia para lograrlo.

Iba a intentar explicar esto a un par de personas, pero el único ejemplo en el que puedo pensar es en el uso de diferentes clases de validación basadas en el estado de una "orden".

He leído un par de artículos en línea, pero estos no suelen describir una razón real para usar la estrategia, como generar informes / facturas / validación, etc.

¿Hay ejemplos del mundo real en los que crea que un patrón de estrategia es común?

Respuestas:


99

¿Qué pasa con esto?

Tienes que cifrar un archivo.

Para archivos pequeños, puede usar la estrategia "en memoria", donde el archivo completo se lee y se guarda en la memoria (digamos para archivos <1 gb)

Para archivos grandes, puede utilizar otra estrategia, en la que partes del archivo se leen en la memoria y los resultados cifrados parciales se almacenan en archivos tmp.

Estas pueden ser dos estrategias diferentes para la misma tarea.

El código del cliente se vería igual:

 File file = getFile();
 Cipher c = CipherFactory.getCipher( file.size() );
 c.performAction();



// implementations:
interface  Cipher  {
     public void performAction();
}

class InMemoryCipherStrategy implements Cipher { 
         public void performAction() {
             // load in byte[] ....
         }
}

class SwaptToDiskCipher implements Cipher { 
         public void performAction() {
             // swapt partial results to file.
         }

}

los

     Cipher c = CipherFactory.getCipher( file.size() );

Devolvería la instancia de estrategia correcta para el cifrado.

Espero que esto ayude.

(Ni siquiera sé si Cipher es la palabra correcta: P)


8
¿Tu ejemplo no es más un patrón de fábrica? También creo que no funcionará en C #, por ejemplo. Su método "getCipher ()" es un método estático, pero en C # no puede definir un método estático en una interfaz (tampoco en Java, creo, pero de esto no estoy seguro).
FrenchData

10
Van juntos. La Fábrica crea la estrategia, pero la estrategia en sí misma contiene el algoritmo para realizar la misma operación (básicamente). La estrategia también se puede cambiar en tiempo de ejecución. Sobre el método de fábrica estás en lo correcto, lo he cambiado.
OscarRyz

Para agregar el punto de Osacars, sin fábrica, esto se puede crear sin fábrica Cipher C =null; if (file.size() <= 2048) { C = new InMemoryCipherStrategy(); } else { c= SwaptToDiskCipher (); }
Abhijit Mazumder

De acuerdo con @FrenchData. Si bien es un gran ejemplo, la presencia de CipherFactorypuede confundir a quienes no están familiarizados con el patrón de estrategia.
user487772

1
El patrón de fábrica se trata de creación, la estrategia se trata de comportamiento. Hay una ligera diferencia, ¿verdad?
nhoxbypass

61

Una vez más, una publicación antigua, pero todavía aparece en las búsquedas, así que agregaré dos ejemplos más (el código está en C #). Me encanta el patrón de estrategia, ya que me ha salvado el trasero muchas veces cuando los gerentes de proyecto dicen: "Queremos que la aplicación haga 'X', pero 'X' aún no está claro y puede cambiar en un futuro cercano. " Este video que explica el patrón de estrategia , usa StarCraft como ejemplo.

Cosas que entran en esta categoría:

  • Clasificación: Queremos ordenar estos números, pero no sabemos si vamos a usar BrickSort, BubbleSort o alguna otra clasificación.

  • Validación: Necesitamos verificar los elementos de acuerdo con "Alguna regla", pero aún no está claro cuál será esa regla y podemos pensar en otras nuevas.

  • Juegos: Queremos que el jugador camine o corra cuando se mueva, pero tal vez en el futuro, también debería poder nadar, volar, teletransportarse, excavar bajo tierra, etc.

  • Almacenamiento de información: queremos que la aplicación almacene información en la base de datos, pero más adelante es posible que deba poder guardar un archivo o realizar una llamada web.

  • Salida: Necesitamos generar X como una cadena simple, pero más tarde puede ser un CSV, XML, JSON, etc.


Ejemplos

Tengo un proyecto donde los usuarios pueden asignar productos a personas en una base de datos. Esta asignación de un producto a una persona tiene un estado que es "Aprobado" o "Rechazado", que depende de algunas reglas comerciales. Por ejemplo: si un usuario asigna un producto a una persona con cierta edad, su estado debe ser rechazado; Si la diferencia entre dos campos en el elemento es mayor que 50, su estado se rechaza, etc.

Ahora, en el momento del desarrollo, estas reglas de negocio aún no están completamente claras y podrían surgir nuevas reglas en cualquier momento. El poder del patrón de estrategia es que hice un RuleAgent, al que se le da una lista de IRules.

public interface IRule {
    bool IsApproved(Assignment assignment); 
 }

En el momento de asignar un producto a una persona, creo un RuleAgent, le doy una lista de reglas (que implementan IRule) y le pido que valide una asignación. Pasará por todas sus reglas. Lo cual, debido a que todos implementan la misma interfaz, todos tienen el IsApprovedmétodo y devuelven falso si alguno de ellos devuelve falso.

Ahora, cuando, por ejemplo, el gerente aparece de repente y dice, también debemos rechazar todas las asignaciones a los pasantes, o todas las asignaciones a las personas que trabajan horas extras ... Usted crea nuevas clases como esta:

public OvertimeRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Timesheet >= 40)
        {
            return false;
        }
        return true;
    }
}

public InternRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Title == "Intern")
        {
            return false;
        }
        return true;
    }
}

Verá que no tiene que seguir agregando o eliminando declaraciones if o código, simplemente cree una nueva clase de reglas que implemente la interfaz IRUle y cámbielas cuando sea necesario.


Otro gran ejemplo: la serie de videos de Scott Allen en http://www.asp.net/mvc/pluralsight donde usa el patrón de estrategia en la parte de prueba unitaria de la aplicación

Construye un sitio web que tiene una página que muestra elementos basados ​​en la popularidad. Sin embargo, "Popular" puede ser muchas cosas (la mayoría de las vistas, la mayoría de los suscriptores, la fecha de creación, la mayor cantidad de actividad, la menor cantidad de comentarios, etc.) y, en caso de que la administración aún no sepa exactamente cómo ordenar, es posible que desee experimentar con diferentes pedidos en una fecha posterior. Usted crea una interfaz (IOrderAlgorithm o algo así) con un método de orden, y deja que un objeto Orderer delegue la ordenación a una implementación concreta de la interfaz IOrderAlgorithm. Puede crear un "CommentOrderer", "ActivityOrderer", etc. Y simplemente cambiarlos cuando surjan nuevos requisitos.


Sé que esto está un poco fuera del alcance de la pregunta, pero ¿qué sigue? Tenemos esto InternRuleahora, pero ¿cómo lo estamos activando OvertimeRule? ¿Cómo nos aseguramos de que cualquier lógica llamada OvertimeRule.IsApprovedahora también llame InternRule.IsApproved?
Spencer Ruport

13

Notas clave:

  1. La estrategia es un patrón de diseño conductual. Se utiliza para cambiar entre familias de algoritmos.

  2. Este patrón contiene una interfaz de estrategia abstracta y muchas implementaciones de estrategias concretas ( algoritmos ) de esa interfaz.

  3. La aplicación utiliza únicamente la interfaz de estrategia . Dependiendo de algún parámetro de configuración, la estrategia concreta se etiquetará como interfaz .

Diagrama UML de wikipedia

ingrese la descripción de la imagen aquí

Un ejemplo de palabra real: aerolíneas que ofrecen descuentos durante algunos meses (julio-diciembre) . Puede tener un módulo de tarifa , que decide las opciones de precios según el número de mes.

Eche un vistazo a un ejemplo sencillo. Este ejemplo puede extenderse a aplicaciones de venta minorista en línea, que ofrecen descuentos en artículos del carrito de compras en días especiales / horas felices fácilmente.

import java.util.*;

/* Interface for Strategy */
interface OfferStrategy {
    public String getName();
    public double getDiscountPercentage();
}
/* Concrete implementation of base Strategy */
class NoDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0;
    }
}
/* Concrete implementation of base Strategy */
class QuarterDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0.25;
    }
}
/* Context is optional. But if it is present, it acts as single point of contact
   for client. 

   Multiple uses of Context
   1. It can populate data to execute an operation of strategy
   2. It can take independent decision on Strategy creation. 
   3. In absence of Context, client should be aware of concrete strategies. Context acts a wrapper and hides internals
   4. Code re-factoring will become easy
*/
class StrategyContext {
    double price; // price for some item or air ticket etc.
    Map<String,OfferStrategy> strategyContext = new HashMap<String,OfferStrategy>();
    StrategyContext(double price){
        this.price= price;
        strategyContext.put(NoDiscountStrategy.class.getName(),new NoDiscountStrategy());
        strategyContext.put(QuarterDiscountStrategy.class.getName(),new QuarterDiscountStrategy());        
    }
    public void applyStrategy(OfferStrategy strategy){
        /* 
        Currently applyStrategy has simple implementation. You can use Context for populating some more information,
        which is required to call a particular operation            
        */
        System.out.println("Price before offer :"+price);
        double finalPrice = price - (price*strategy.getDiscountPercentage());
        System.out.println("Price after offer:"+finalPrice);
    }
    public OfferStrategy getStrategy(int monthNo){
        /*
            In absence of this Context method, client has to import relevant concrete Strategies everywhere.
            Context acts as single point of contact for the Client to get relevant Strategy
        */
        if ( monthNo < 6 )  {
            return strategyContext.get(NoDiscountStrategy.class.getName());
        }else{
            return strategyContext.get(QuarterDiscountStrategy.class.getName());
        }

    }
}
public class StrategyDemo{    
    public static void main(String args[]){
        StrategyContext context = new StrategyContext(100);
        System.out.println("Enter month number between 1 and 12");
        int month = Integer.parseInt(args[0]);
        System.out.println("Month ="+month);
        OfferStrategy strategy = context.getStrategy(month);
        context.applyStrategy(strategy);
    }

}

salida:

Enter month number between 1 and 12
Month =1
Price before offer :100.0
Price after offer:100.0

Enter month number between 1 and 12
Month =7
Price before offer :100.0
Price after offer:75.0

Artículos útiles:

patrón de estrategia de dzone

patrón de estrategia por sourcemaking


12

Puedo pensar en varios ejemplos bastante simples:

  • Ordenar una lista. La estrategia es la comparación que se utiliza para decidir cuál de los dos elementos de la lista es "Primero".
  • Es posible que tenga una aplicación donde el algoritmo de clasificación en sí (QuickSort, HeapSort, etc.) se puede elegir en tiempo de ejecución
  • Anexos, diseños y filtros en Log4Net y Log4j
  • Administradores de diseño en kits de herramientas de interfaz de usuario
  • Compresión de datos. Es posible que tenga una interfaz ICompressor cuyo único método se parezca a esto:

    byte [] comprimir (byte [] entrada);

    Sus clases de compresión concretas pueden ser cosas como RunLengthCompression, DeflateCompression, etc.


9

Un uso común del patrón de estrategia es definir estrategias de clasificación personalizadas (en lenguajes sin funciones de orden superior), por ejemplo, para ordenar una lista de cadenas por longitud en Java, pasando una clase interna anónima (una implementación de la interfaz de la estrategia):

List<String> names = Arrays.asList("Anne", "Joe", "Harry");
Collections.sort(names, new Comparator<String>() {
  public int compare(String o1, String o2) {
    return o1.length() - o2.length();
  }
});
Assert.assertEquals(Arrays.asList("Joe", "Anne", "Harry"), names);

De manera similar, se pueden utilizar estrategias para consultas nativas con bases de datos de objetos, por ejemplo, en db4o:

List<Document> set = db.query(new Predicate<Document>() {
  public boolean match(Document candidate) {
    return candidate.getSource().contains(source);
  }
});

8

Tengo una aplicación que sincroniza su base de usuarios cada día con nuestro directorio empresarial. Los usuarios son elegibles o no en función de su estado en la Universidad. Todos los días, el programa de aprovisionamiento se ejecuta y se asegura de que aquellos que se supone que son elegibles sean aprovisionados en la aplicación y aquellos que no lo sean se desaprovisionen (en realidad, de acuerdo con un elegante algoritmo de degradación, pero eso no viene al caso). El sábado hago una actualización más exhaustiva que sincroniza algunas propiedades de cada usuario además de asegurarme de que tengan la elegibilidad adecuada. Al final del mes, proceso algunas facturas en función del uso de ese mes.

Utilizo un patrón de estrategia componible para hacer esta sincronización. El programa principal básicamente elige una estrategia maestra en función del día de la semana (sincronizar solo cambios / sincronizar todos) y la hora del semestre en relación con el calendario académico. Si el ciclo de facturación finaliza, también lo compone con una estrategia de facturación. Luego ejecuta la estrategia elegida a través de una interfaz estándar.

No sé qué tan común es esto, pero sentí que encajaba perfectamente con el patrón de estrategia.


Este es un muy buen ejemplo. Además, le dice claramente la diferencia entre el comando y el patrón de estrategia en pocas palabras: la intención. "El programa principal básicamente elige una estrategia maestra según el día de la semana"
Utsav T

7

Sé que esta es una pregunta antigua, pero creo que tengo otro ejemplo interesante que implementé recientemente.

Este es un ejemplo muy práctico del patrón de estrategia que se utiliza en un sistema de entrega de documentos.

Tenía un sistema de entrega de PDF que recibía un archivo que contenía muchos documentos y algunos metadatos. Basándose en los metadatos, decidió dónde colocar el documento; por ejemplo, en función de los datos, podría almacenar el documento en A, Bo Csistemas de almacenamiento, o una mezcla de los tres.

Diferentes clientes usaban este sistema y tenían diferentes requisitos de gestión de reversión / error en caso de errores: uno quería que el sistema de entrega se detuviera ante el primer error, dejara todos los documentos ya entregados en sus almacenes, pero detuviera el proceso y no entregara nada más. ; otro quería que se revertiera Ben caso de errores al almacenar C, pero dejar lo que ya se envió A. Es fácil imaginar que un tercero o cuarto también tendrá diferentes necesidades.

Para resolver el problema, he creado una clase de entrega básica que contiene la lógica de entrega, además de métodos para revertir cosas de todos los almacenamientos. En realidad, el sistema de entrega no llama a esos métodos directamente en caso de errores. En su lugar, la clase usa Inyección de dependencia para recibir una clase de "Estrategia de reversión / manejo de errores" (basada en el cliente que usa el sistema), que se llama en caso de errores, que a su vez llama a los métodos de reversión si es apropiado para esa estrategia.

La clase de entrega en sí informa lo que está sucediendo a la clase de estrategia (qué documentos se entregaron a qué almacenamientos y qué fallas sucedieron), y siempre que ocurre un error, pregunta a la estrategia si continuar o no. Si la estrategia dice "deténgalo", la clase llama al método "cleanUp" de la estrategia, que usa la información reportada previamente para decidir qué métodos de reversión llamar desde la clase de entrega, o simplemente no hacer nada.

rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);

if (rollbackStrategy.mustAbort()) {
    rollbackStrategy.rollback(); // rollback whatever is needed based on reports
    return false;
}

Así que ahora tengo dos estrategias diferentes: una es la QuitterStrategy(que se cierra con el primer error y no limpia nada) y la otra es la MaximizeDeliveryToAStrategy(que intenta en la medida de lo posible no abortar el proceso y nunca revertir las cosas entregadas al almacenamiento A, pero deshace cosas de Bsi la entrega Cfalla).

Según tengo entendido, este es un ejemplo del patrón de estrategia. Si usted (sí, está leyendo) cree que estoy equivocado, por favor comente a continuación y hágamelo saber. Tengo curiosidad por saber qué constituiría un uso "puro" del patrón de estrategia y qué aspectos de mi implementación violan la definición. Creo que se ve un poco divertido porque la interfaz de estrategia es un poco gruesa. Todos los ejemplos que he visto hasta ahora utilizan un solo método, pero sigo pensando que encapsula un algoritmo (si una parte de la lógica empresarial puede considerarse un algoritmo, lo cual creo que sí).

Dado que la estrategia también recibe notificaciones sobre eventos durante la ejecución de la entrega, también se puede considerar un observador , pero esa es otra historia.

Al hacer una pequeña investigación, parece que se trata de un "patrón compuesto" (como MVC, un patrón que utiliza múltiples patrones de diseño debajo de una manera particular) llamado Asesor . Es un asesor sobre si la entrega debe continuar o no, pero también es un manejador de errores activo, ya que puede revertir cosas cuando se le solicite.

De todos modos, este es un ejemplo bastante complejo que puede hacer que sienta que los usos del patrón de estrategia son demasiado simples / tontos. Puede ser realmente complejo e incluso más aplicable cuando se usa junto con otros patrones.


6

El patrón de estrategia es el patrón más comúnmente utilizado, especialmente para validaciones y algoritmos de clasificación.

Déjame explicarte con un sencillo ejemplo práctico.

enum Speed {
  SLOW, MEDIUM, FAST;
}

class Sorter {
 public void sort(int[] input, Speed speed) {
    SortStrategy strategy = null;
    switch (speed) {
    case SLOW:
        strategy = new SlowBubbleSortStrategy();
        break;
    case MEDIUM:
        strategy = new MediumInsertationSortStrategy();
        break;

    case FAST:
        strategy = new FastQuickSortStrategy();
        break;
    default:
        strategy = new MediumInsertationSortStrategy();
    }
    strategy.sort(input);
 }

}

interface SortStrategy {

    public void sort(int[] input);
}

class SlowBubbleSortStrategy implements SortStrategy {

   public void sort(int[] input) {
    for (int i = 0; i < input.length; i++) {
        for (int j = i + 1; j < input.length; j++) {
            if (input[i] > input[j]) {
                int tmp = input[i];
                input[i] = input[j];
                input[j] = tmp;
            }
        }
    }
    System.out.println("Slow sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
  }

 }

class MediumInsertationSortStrategy implements SortStrategy {

public void sort(int[] input) {
    for (int i = 0; i < input.length - 1; i++) {
        int k = i + 1;
        int nxtVal = input[k];
        while (input[k - 1] > nxtVal) {
            input[k] = input[k - 1];
            k--;
            if (k == 0)
                break;
        }
        input[k] = nxtVal;
    }
    System.out.println("Medium sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }

 }

}

class FastQuickSortStrategy implements SortStrategy {

public void sort(int[] input) {
    sort(input, 0, input.length-1);
    System.out.println("Fast sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
}

private void sort(int[] input, int startIndx, int endIndx) {
    int endIndexOrig = endIndx;
    int startIndexOrig = startIndx;
    if( startIndx >= endIndx)
        return;
    int pavitVal = input[endIndx];
    while (startIndx <= endIndx) {
        while (input[startIndx] < pavitVal)
            startIndx++;
        while (input[endIndx] > pavitVal)
            endIndx--;
        if( startIndx <= endIndx){
            int tmp = input[startIndx];
            input[startIndx] = input[endIndx];
            input[endIndx] = tmp;
            startIndx++;
            endIndx--;
        }
    }
    sort(input, startIndexOrig, endIndx);
    sort(input, startIndx, endIndexOrig);
 }

}  

El código de prueba para esto es

public class StrategyPattern {
  public static void main(String[] args) {
    Sorter sorter = new Sorter();
    int[] input = new int[] {7,1,23,22,22,11,0,21,1,2,334,45,6,11,2};
    System.out.print("Input is : ");
    for (int i : input) {
        System.out.print(i + ",");
    }
    System.out.println();
    sorter.sort(input, Speed.SLOW);
 }

}

El mismo ejemplo se toma de http://coder2design.com/strategy-pattern/


Diferentes usos del patrón de estrategia: Validaciones: cuando hay muchas validaciones, es necesario realizarlas en su código. Diferentes algoritmos: especialmente cuando se pueden utilizar diferentes algoritmos de clasificación, por ejemplo, clasificación de burbujas o clasificación rápida. Almacenamiento de información: cuando podemos obtener información en diferentes lugares, por ejemplo, base de datos o sistema de archivos. Análisis: durante el análisis, podemos usar diferentes estrategias para diferentes entradas. Estrategias de filtrado. Estrategias de diseño.
Jatinder Pal

5

Un buen ejemplo de patrón de estrategia sería en un juego donde podemos tener diferentes personajes y cada personaje puede tener múltiples armas para atacar pero a la vez puede usar una sola arma. Así que tenemos el personaje como contexto, por ejemplo, Rey, Comandante, Caballero, Soldado y arma como estrategia donde ataque () podría ser el método / algoritmo que depende de las armas que se utilicen. Entonces, si las clases de armas concretas fueran Sword, Axe, Crossbow, BowAndArrow, etc., todas implementarían el método attack (). Estoy seguro de que no se necesitan más explicaciones.


1
Pensé que se suponía que la respuesta aceptada hablaba de este ejemplo :)
Jeancarlo Fontalvo

2

Usé el enfoque de estrategia en un motor bastante complejo en una aplicación que es un buen ejemplo. Básicamente, la función del motor era buscar primero una lista de personas que tenían un widget, su segunda función era averiguar cuáles eran las 10 mejores personas con un widget en función de un número desconocido de parámetros (cosas como precio, distancia, negocios anteriores juntos , cantidad en stock, opciones de envío, etc., etc., etc.)

Básicamente, lo que hicimos fue dividir el problema en dos estrategias, la primera de las cuales fue la recuperación de datos, ya que sabíamos que teníamos múltiples fuentes de nuestros widgets y necesitábamos poder obtener los datos y transformarlos en una estructura común.

Luego también nos dimos cuenta de que teníamos múltiples algoritmos, algunos se basaban en ponderar los parámetros, otros eran muy extraños y propicios y no podía hacerles justicia sin sacar visios y gráficos y bueno, ya entiendes, teníamos muchos algoritmos para seleccionando a las mejores personas.

Nuestro servicio en sí era muy importante, esencialmente definía las entradas, salidas e hizo cierta normalización de los datos, también usó un patrón de proveedor para conectar los proveedores de datos específicos de la aplicación y los proveedores de algoritmos que usaron la estrategia. Este fue un sistema bastante efectivo.

Tuvimos algunos debates si estábamos usando una estrategia o un patrón de plantilla que nunca resolvimos.


2

¿Está seguro de que el estado de una "orden" no es un patrón de estado? Tengo el presentimiento de que un pedido no se manejará de manera diferente según su estado.

Tomemos, por ejemplo, el método Enviar en el pedido:

order.Ship();
  • Si el método de envío varía en función de su estado, entonces tiene un patrón de estrategia.
  • Sin embargo, si el método Ship () tiene éxito solo cuando el pedido se ha pagado y el pedido aún no se ha enviado, tiene un patrón de estado.

El mejor ejemplo del patrón de estado (y otros patrones) que encontré fue en el libro " Head First Design Patterns ", que es asombroso. Un segundo cercano será la serie de patrones de blogs de David Cumps .


2

Supongamos que desea escribir un algoritmo para calcular el enésimo día X de un mes y año determinados, por ejemplo, el segundo lunes de octubre de 2014. Quiere utilizar la clase Time de Android android.text.format.Timepara representar la fecha, pero también quiere escribir un algoritmo genérico que también se puede aplicar a java.util.Calendar.

Esto es lo que hice.

En DatetimeMath.java:

public interface DatetimeMath { 
    public Object createDatetime(int year, int month, int day);

    public int getDayOfWeek(Object datetime);

    public void increment(Object datetime);
}

En TimeMath.java:

public class TimeMath implements DatetimeMath {
    @Override
    public Object createDatetime(int year, int month, int day) {
        Time t = new Time();
        t.set(day, month, year);
        t.normalize(false);
        return t;
    }

    @Override
    public int getDayOfWeek(Object o) {
        Time t = (Time)o;
        return t.weekDay;
    }   

    @Override
    public void increment(Object o) {
        Time t = (Time)o;
        t.set(t.monthDay + 1, t.month, t.year);
        t.normalize(false);
    }
}

En OrdinalDayOfWeekCalculator.java, la clase con el algoritmo genérico:

public class OrdinalDayOfWeekCalculator {   
    private DatetimeMath datetimeMath;

    public OrdinalDayOfWeekCalculator(DatetimeMath m) {
        datetimeMath = m;
    }

    public Object getDate(int year, int month, int dayOfWeek, int ordinal) {
        Object datetime = datetimeMath.createDatetime(year, month, 1);
        if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
            return datetime;
        } 
        int xDayCount = 0;
        while (xDayCount != ordinal) {
            datetimeMath.increment(datetime);
            if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
                xDayCount++;
            }
        }
        return datetime;
    }
}

En mi aplicación de Android, llamaría algo como

OrdinalDayOfWeekCalculator odowc = 
        new OrdinalDayOfWeekCalculator(new TimeMath());
Time canadianThanksgiving = (Time)odowc.getDate(
        year, Calendar.OCTOBER, Time.MONDAY, 2);

Si quiero reutilizar el mismo algoritmo para java.util.Calendar, simplemente escribiría una clase CalendarMath que implemente los tres métodos en DatetimeMath y luego usaría

OrdinalDayOfWeekCalculator odowc2 = 
        new OrdinalDayOfWeekCalculator(new CalendarMath());
Calendar canadianThanksgivingCal = (Calendar)odowc2.getDate(
        year, Calendar.OCTOBER, Calendar.MONDAY, 2);

2
public class StrategyDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        Item item1 = new Item("1234", 10);
        Item item2 = new Item("5678", 40);

        cart.addItem(item1);
        cart.addItem(item2);

        // pay by paypal
        cart.pay(new PaypalStrategy("myemail@example.com", "mypwd"));

        // pay by credit card
        cart.pay(new CreditCardStrategy("Pankaj Kumar", "1234567890123456", "786", "12/15"));
    }
}

interface PaymentStrategy {
    public void pay(int amount);
}

class CreditCardStrategy implements PaymentStrategy {

    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;

    public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate) {
        this.name = nm;
        this.cardNumber = ccNum;
        this.cvv = cvv;
        this.dateOfExpiry = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }

}

class PaypalStrategy implements PaymentStrategy {

    private String emailId;
    private String password;

    public PaypalStrategy(String email, String pwd) {
        this.emailId = email;
        this.password = pwd;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using Paypal.");
    }

}

class Item {

    private String upcCode;
    private int price;

    public Item(String upc, int cost) {
        this.upcCode = upc;
        this.price = cost;
    }

    public String getUpcCode() {
        return upcCode;
    }

    public int getPrice() {
        return price;
    }

}

class ShoppingCart {

    // List of items
    List<Item> items;

    public ShoppingCart() {
        this.items = new ArrayList<Item>();
    }

    public void addItem(Item item) {
        this.items.add(item);
    }

    public void removeItem(Item item) {
        this.items.remove(item);
    }

    public int calculateTotal() {
        int sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }

    public void pay(PaymentStrategy paymentMethod) {
        int amount = calculateTotal();
        paymentMethod.pay(amount);
    }
}

1

Hace unas semanas, agregué una interfaz Java común que fue implementada por uno de nuestros objetos de dominio. Este objeto de dominio se cargó desde la base de datos y la representación de la base de datos era un esquema en estrella con más de 10 ramas. Una de las consecuencias de tener un objeto de dominio tan pesado es que hemos tenido que hacer otros objetos de dominio que representaban el mismo esquema, aunque menos pesado. Así que hice que los otros objetos ligeros implementaran la misma interfaz. Dicho de otra manera, teníamos:

public interface CollectibleElephant { 
    long getId();
    String getName();
    long getTagId();
}

public class Elephant implements CollectibleElephant { ... }
public class BabyElephant implements CollectibleElephant { ... }

Originalmente, quería usar CollectibleElephantpara ordenar Elephants. Muy rápidamente, mis compañeros de equipo se apresuraron CollectibleElephanta ejecutar controles de seguridad, filtrarlos a medida que los enviaban a la GUI, etc.


1

Tuvimos que crear una interfaz de aprovisionamiento de terceros para una plataforma empresarial con una base de datos muy complicada. El envío de datos para ser aprovisionados fue como una lista de nuestros tipos de datos que se colocaron en una cola de prioridad en nuestra aplicación para que pudieran escribirse en la base de datos en el orden correcto debido a las dependencias.

El proceso para escribir esos datos fue bastante simple, seguir apareciendo en la parte superior de la cola de prioridad y luego elegir una estrategia basada en el tipo de objeto que extrae.


0

De wikipedia

En programación informática, el patrón de estrategia (también conocido como patrón de política) es un patrón de diseño de software de comportamiento que permite seleccionar un algoritmo en tiempo de ejecución. En lugar de implementar un solo algoritmo directamente, el código recibe instrucciones en tiempo de ejecución sobre cuál en una familia de algoritmos usar

En la aplicación Windows Paint, puede ver un patrón de estrategia en el que puede elegir la forma y el color de forma independiente en diferentes secciones. Aquí la forma y el color son los algoritmos que pueden cambiar en tiempo de ejecución.

Si desea dibujar un círculo con color rojo, en lugar de proporcionar una opción de 'RedCircle', le permiten elegir el círculo y el color de su elección.

Shape redCircle = new RedCircle(); // Without stretegy Pattern
Shaped redCircle = new Shape("red","circle"); // With Strategy pattern

Sin patrón de estrategia aumentará el número de clases con el producto cartesiano de forma y color. Además, la interfaz cambia para cada implementación.


0

Imagina un juego de disparos con enemigos de IA, por ejemplo. Desea que luchen continuamente de diferentes maneras según lo que suceda. Con el patrón de estrategia, puede realizar un ciclo continuo y cambiar dinámicamente cómo se realizará una acción específica.

interface FightingStategy{
    public void fight();
}
public Defense implements FightingStrategy{
    public void figth(){
        ... hide behind wall to shoot
    }
}
public Berserker implements FightingStrategy{
    public void fight(){
        ... run towards you, headrolls and shoots
    }
}
public Dead implements FightingStrategy{
    public void fight(){
        ... is dead, doesn't move
    }
}

public AiShooter{

    FightingStrategy fightingStrategy;

    public AiShooter(){
        fightStrategy = new Berserker();
    }

    public void fight(){
        this.fightingStrategy.fight();
    }

    public void changeStrategy(FightingStrategy f){
        this.fightingStrategy = f;
    }
}

public static void main(){

    ... create list of AiShooters...
    while (condition){
        list.forEach(shooter -> shooter.fight());
    }
    ... you shoot back
    list.ForEach(shooter -> shooter.changeStrategy(new 
Defense()));

    ... you kill one
    list.get(n).changeStrategy(new Dead());
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.