Patrón de diseño de estrategia modificado

11

Recientemente comencé a buscar patrones de diseño, y una cosa que estoy codificando se adaptaría perfectamente al patrón de estrategia, excepto por una pequeña diferencia.

Esencialmente, algunos (pero no todos) de mis algoritmos, necesitan que se les pase uno o dos parámetros adicionales.

Entonces, o bien necesito

  • les paso un parámetro extra cuando invoco su método de cálculo

o

  • almacénelos como variables dentro de la clase ConcreteAlgorithm y pueda actualizarlos antes de que llame al algoritmo.

¿Existe un patrón de diseño para esta necesidad / ¿Cómo podría implementar esto mientras me apego al Patrón de Estrategia?

He considerado pasar el objeto del cliente a todos los algoritmos, y almacenar las variables allí, luego usarlo solo cuando el algoritmo particular lo necesite. Sin embargo, creo que esto es difícil de manejar y derrota el punto del patrón de estrategia.

Para ser claros, estoy implementando en Java, por lo que no tengo el lujo de parámetros opcionales (lo que resolvería esto muy bien).

Megan Walker
fuente
Los parámetros opcionales como en C ++ no resolverían nada, ya que son solo una abreviatura para definir múltiples métodos sobrecargados.
maaartinus
Me esforzaría por evitar almacenar los parámetros adicionales en algún lugar donde tuviera que cambiarlos antes de usarlos. De esta forma, hace que ConcreteAlgorithm tenga estado, por lo que no se puede pasar fácilmente a otro método o hilos. Además, es demasiado fácil olvidar establecer los parámetros.
maaartinus

Respuestas:

5

Samuel, ¿es posible encapsular el parámetro que cada una de las estrategias toma en una clase común y luego extender esa clase de parámetro común para agregar más comportamiento que algunas de tus estrategias necesitan específicamente?

P.ej

StrategyParameter //Base strategy parameter that most of the strategies need
        ^
        |
        |
SpecialStrategyParameter // will be used for strategies that need more parameter

Y luego, defina la jerarquía de la estrategia como:

Interface MyStrategy {
   void myStrategyMethod(StrategyParameter parameter);
}

class MyNormalStrategy extends MyStrategy {
   void myStrategyMethod(StrategyParameter parameter) {
       //implement the logic here
   }
}

llame a la estrategia anterior como: myNormalStrategyInstance.myStrategyMethod(strategyParameter);

class MySpecializedStrategy extends MyStrategy {
   void myStrategyMethod(StrategyParameter parameter) {
       //implement the logic here
   }
}

llame a la estrategia anterior pasando la SpecialStrategyParameterinstancia en su lugar como:mySpecializedStrategy.myStrategyMethod(specialStrategyParameter);

Actualice si algo no está claro. Estaremos encantados de explicar / aclarar.

pico
fuente
2
-1 requiere abatido, rompe la encapsulación del diseño. Aunque es una mejora en el diseño de la pregunta, hay mejores maneras de desollar a este gato.
tallseth
@tallseth También veo abatido. Pero no veo mejores formas. ¿Podría por favor señalar una mejor solución? ¿Un artículo o algo?
Narek
Actualmente, si. @ Jordão tiene la respuesta que preferiría, según los detalles que tenemos en la pregunta. Esa respuesta juega con las fortalezas del patrón de estrategia. Si seguimos el enfoque en esta respuesta, me gustaría tener StrategyParametertodos los parámetros posibles, como un DTO. Algunas implementaciones de la estrategia podrían ignorarlos. En algunos casos, ese es el mejor enfoque. El contexto es el rey para este tipo de problemas.
tallseth
4

Necesita aclarar su estrategia .

Todo depende de cómo uses tus algoritmos. Para que su clase de cliente use diferentes implementaciones de estrategia de manera intercambiable, todas deben tener una abstracción común . Si no siguen la misma interfaz, tal vez lo que necesita son diferentes abstracciones .

He usado estrategias configurables antes, donde parametrizas las clases concretas en la construcción:

interface Strategy {
  int calculate();
}

class ConcreteStrategyThatNeedsAParameter implements Strategy {
  private final int param;
  public ConcreteStrategyThatNeedsAParameter(int param) {
    this.param = param;
  }
  public int calculate() { 
    // uses param...
  }
}

Ahora, alguien aún necesita crear una instancia de esta clase y pasarla a su cliente. Pero su cliente solo necesita saber sobre la Strategyinterfaz.

También funciona si su método de estrategia toma parámetros, pero luego su cliente conoce esos parámetros y los pasa a todas las implementaciones con las que trabaja.

Jordão
fuente
El cliente es el que tiene el contexto para proporcionar el parámetro.
andyczerwonka
1

Mientras la firma se defina claramente en la interfaz, aún cumple con el patrón de Estrategia.

Los patrones tal como están escritos son la forma más simple y absoluta que aún exhibe el comportamiento esperado, por lo que puede embellecerlos siempre que mantenga la intención original. Eso, por supuesto, supone que quieres seguir el patrón. No tiene sentido usar un patrón si no encaja, o simplemente porque está allí, pero en tu caso creo que estás bien.

Ian
fuente
0

ampliando la respuesta anterior proporcionada por peakit: puede usar la abstracción. Estoy usando el código de Peakit aquí -

Interface MyStrategy { abstract void myStrategyMethod (parámetro StrategyParameter); }

La clase MyNormalStrategy extiende MyStrategy {anulación pública anula myStrategyMethod (parámetro StrategyParameter) {// implementa la lógica aquí}}

clase MySpecializedStrategy extiende MyStrategy {anulación pública void myStrategyMethod (parámetro StrategyParameter, ExtraStrategyParameter extraParameter) {// implementa la lógica aquí} }

Si entiendo su pregunta correctamente, ¿desea pasar un parámetro adicional a ciertos algoritmos, verdad? Por favor, avíseme si esto es lo que estaba buscando.


fuente
0

Si observa el libro de patrones de diseño, no está mal per se que exista alguna SimpleStrategy que use menos o ninguno de los parámetros pasados, o que los parámetros sean un único tamaño para todos / mínimo-multiplicador común. La elección del diseño aquí es si esto le perjudica en términos de procesamiento adicional que termina sin ser utilizado.

pjv
fuente