2011-05-04 16 views
5

Estoy intentando aplicar el patrón de estrategia a una situación particular, pero tengo un problema con cómo evitar acoplar cada estrategia concreta al objeto de contexto que proporciona datos para él. El siguiente es un caso simplificado de un patrón que ocurre de diferentes maneras, pero debe manejarse de manera similar.Evitando el acoplamiento con el patrón de estrategia

Tenemos un objeto Acquisition que proporciona datos relevantes para un marco de tiempo específico, básicamente un conjunto de datos externos recopilados utilizando diferentes piezas de hardware. Ya es demasiado grande debido a la cantidad de datos que contiene, por lo que no quiero darle ninguna otra responsabilidad. Ahora necesitamos tomar algunos de estos datos y, de acuerdo con alguna configuración, enviar un voltaje correspondiente a una pieza de hardware.

Por lo tanto, imaginar el (muy simplificado) clases siguientes:

class Acquisition 
{ 
    public Int32 IntegrationTime { get; set; } 
    public Double Battery { get; set; } 
    public Double Signal { get; set; } 
} 

interface IAnalogOutputter 
{ 
    double getVoltage(Acquisition acq); 
} 

class BatteryAnalogOutputter : IAnalogOutputter 
{ 
    double getVoltage(Acquisition acq) 
    { 
     return acq.Battery; 
    } 
} 

Ahora, todas las clases estrategia concreta tiene que acoplarse a mi clase de adquisición, que es también una de las clases más probabilidades de ser modificado desde es esencial para nuestra aplicación. Esto sigue siendo una mejora con respecto al diseño antiguo, que era una declaración de cambio gigante dentro de clase Acquisition. Cada tipo de datos puede tener un método de conversión diferente (mientras que la batería es un simple paso, otros no son tan simples), así que creo que el patrón de estrategia o similar debería ser el camino a seguir.

También notaré que en la implementación final, IAnalogOutputter sería una clase abstracta en lugar de una interfaz. Estas clases estarán en una lista configurable por el usuario y serializada en un archivo XML. La lista debe poder editarse en tiempo de ejecución y recordarse, por lo que Serializable debe ser parte de nuestra solución final. En caso de que haga una diferencia.

¿Cómo puedo garantizar que cada clase de implementación obtenga los datos que necesita para trabajar, sin vincularlos a una de mis clases más importantes? ¿O me estoy acercando a este tipo de problema de la manera completamente incorrecta?

Respuesta

0

Ok, odio a no dar a alguien el crédito aquí, pero he encontrado una solución híbrida que funciona muy bien para mis propósitos. Se serializa perfectamente y simplifica enormemente la adición de nuevos tipos de salida. La clave era una interfaz única, IOutputValueProvider. También tenga en cuenta la facilidad con que este patrón maneja la recuperación de diferentes formas de almacenar los datos (como un diccionario en lugar de un parámetro).

interface IOutputValueProvider 
{ 
    Double GetBattery(); 
    Double GetSignal(); 
    Int32 GetIntegrationTime(); 
    Double GetDictionaryValue(String key); 
} 

interface IAnalogOutputter 
{ 
    double getVoltage(IOutputValueProvider provider); 
} 

class BatteryAnalogOutputter : IAnalogOutputter 
{ 
    double getVoltage(IOutputValueProvider provider) 
    { 
     return provider.GetBattery(); 
    } 
} 

class DictionaryValueOutputter : IAnalogOutputter 
{ 
    public String DictionaryKey { get; set; } 
    public double getVoltage(IOutputValueProvider provider) 
    { 
     return provider.GetDictionaryValue(DictionaryKey); 
    } 
} 

Así entonces, sólo tiene que asegurarse de Acquisition implementa la interfaz:

class Acquisition : IOutputValueProvider 
{ 
    public Int32 IntegrationTime { get; set; } 
    public Double Battery { get; set; } 
    public Double Signal { get; set; } 
    public Dictionary<String, Double> DictionaryValues; 

    public double GetBattery() { return Battery;} 
    public double GetSignal() { return Signal; } 
    public int GetIntegrationTime() { return IntegrationTime; } 
    public double GetDictionaryValue(String key) 
    { 
     Double d = 0.0; 
     return DictionaryValues.TryGetValue(key, out d) ? d : 0.0; 
    } 
} 

Esto no es perfecta, ya que ahora hay una interfaz gigantesca que se debe mantener y algo de código duplicado en Acquisition, pero hay muchísimo menos riesgo de que se modifique algo que afecte a las otras partes de mi aplicación. También me permite comenzar a subclasificar Acquisition sin tener que cambiar algunas de estas piezas externas. Espero que esto ayude a otros en situaciones similares.

2

Strategy Pattern encapsula a - generalmente complejo - operación/cálculo.

La tensión que desea devolver depende de

  • piezas de configuración
  • Algunos de los datos de adquisición

Así que me gustaría poner esto en otra clase y pasarlo a implementadores de estrategias.

También en términos de serialización, no tiene que serializar las clases de estrategia, quizás solo su nombre o nombre de tipo.


ACTUALIZACIÓN

Pues bien, parece que sus implementaciones necesitan sólo una parte de los datos de adquisición. Eso es un poco inusual para un patrón de estrategia, pero no creo que se ajuste mejor a Visitor, así que la estrategia está bien. Crearía una clase que tenga como propiedad, datos de adquisición (quizás herede de ella) además de la configuración que necesitan los implementadores.

+0

Para aclarar: los datos de adquisición contienen aproximadamente 20 elementos de información, todos los cuales serán utilizados por varios generadores de voltaje. Cada implementación solo usaría una pieza de datos, pero habrá el mismo número (20) de diferentes implementaciones posibles de un generador de voltaje. Entonces, ¿cómo puedo pasar de 'Acquisition' a" otra clase [para] pasarlo a los implementadores de estrategias "? Buen punto acerca de la serialización sin embargo, lo consideraré con seguridad. – drharris

0

Una cosa que podría hacer es usar métodos de fábrica para construir sus estrategias.Sus estrategias individuales pueden tomar en su constructor solo los elementos de datos individuales que necesitan, y el método de fábrica es lo único que necesita saber cómo completar esos datos dado un objeto Acquisition. Algo como esto:

public class OutputterFactory 
{ 
    public static IAnalogOutputter CreateBatteryAnalogOutputter(Acquisition acq) 
    { 
     return new BatteryANalogOutputter(acq.Battery); 
    } 



} 
+0

Había pensado en esto, pero luego parece que para cada nuevo dato, tengo que agregar una propiedad, un método y una clase. Si bien lo desacopla un poco más, hace que la cantidad de cruto aumente en una potencia de 3. Pero, ahora me pregunto si el uso de métodos separados para esto sería la mejor opción. – drharris

Cuestiones relacionadas