2010-11-15 10 views
5

Tengo un conjunto de clases, cada una de las cuales necesita decidir en algún momento cuál de dos o tres enfoques deberían usar internamente para implementar la misma funcionalidad externamente. Idealmente, esto debería incluir la funcionalidad de repliegue, donde si ApproachA falla, se produce un intento de acercarse a ApproachB (y quizás acercarse a C, D, etc.). Hasta ahora, he estado usando codificación como if (!success) { ApproachB code }. El problema con esto es que varios métodos posteriores también deben conocer qué enfoque se eligió y todos ellos desarrollan sus propias declaraciones if (MethodChosen) { } else { } también. Realmente quiero abordar el problema con algo menos difícil de manejar ... excepto que ninguna de las otras opciones que he considerado parece tan "manejable". Estos son los tres enfoques que pensé:Implementando Clases de "retroceso"

  1. implementar un método .Create estática que decide cuál de las dos clases derivadas para crear, en donde las dos clases tienen una interfaz que los respalda. La desventaja de esto es que está escribiendo mucho del mismo código dos veces, y en realidad no está creando un "retroceso", ya que obliga a todas las decisiones a realizarse por adelantado en el método .Create. Esto debería funcionar 9/10 veces, pero habrá otras 1/10 veces en las que quiero que la reserva entre solo cuando la primaria haya intentado y haya fallado.
  2. Lo mismo que arriba, pero con una clase base o abstracta involucrada, ya sea como una clase de respaldo para ambos, o con el primario como la clase base para el respaldo. Esto tiene la misma desventaja de respaldo, pero al menos hay poco o ningún código repetido.
  3. implementar una clase abstracta construida normalmente con clases de niño que se puede cambiar en tiempo de ejecución: es decir

    public void SomeMethodOrConstructor() 
    { 
        if (someConditions) 
         MyChild = ChildClassA; 
        else 
         MyChild = ChildClassB; 
    } 
    
    
    public void Execute() 
    { 
        MyChild.Execute(); 
    } 
    

El problema con la opción 3 está pasando datos entre los dos cuando sea necesario. Dado que algunos de estos métodos están modelando objetos externos, eso será bastante frecuente. ¿Las clases anidadas comparten datos con su clase principal automáticamente? ¿O tendré que pasarlo todo con cada llamada?

¿Algo más que debería considerar?


Actualización: La primera clase está funcionando con la Cadena de responsabilidad. Por el momento, he optado por no utilizar el Patrón de Estrategia o el respaldo durante la ejecución del método, ya que creo que puede ser innecesario al final. Creo que la mayoría de estas ejecuciones fallidas mejorarán al permanecer dentro de sus propias clases, ya que no habrá un cambio completo en el plan de juego, solo algunos retoques menores con los que lidiar. Si ese no es el caso, al menos sabré qué es lo que necesito investigar ahora.

¡Gracias a todos los que ayudaron con la solución definitiva!

Para los curiosos, mi última solución funcionó más o menos así:

  • Crear clase abstracta Handler, más o menos como se describe en el artículo de Wikipedia, pero con una función public abstract Handler GetHandler(), y la adición de otros métodos abstractos como carga, Guardar, etc.
  • Implementar subclases de manejador privado para la clase principal (también podrían ser subclases, ya que solo manejarán cosas para esa clase en particular ... también evita problemas de nombres posteriores). Todas las clases secundarias toman un parámetro del tipo del objeto principal en su constructor, por lo que tienen fácil acceso a los datos del padre.
  • Desde el constructor de la clase padre, configure los controladores/sucesores de Cadena de responsabilidad (de nuevo, como en el ejemplo), luego llame al FirstHandler.GetHandler(this) y almacene el resultado para que la clase sepa qué controlador usar en el futuro.
  • La mayoría de los métodos manejados simplemente reducen a Handler.MethodName().
+2

Puede poner el código en una lista numerada al sangrarlo con _eight_ espacios. – SLaks

+0

Gracias por el consejo de formateo, ¡eso me estaba llevando por la pared! – RobinHood70

Respuesta

5

Use Chain of Responsibility.

patrón de cadena de responsabilidad es un patrón diseño que consiste en una fuente de objetos de comando y una serie de objetos de procesamiento. Cada objeto de procesamiento contiene un conjunto de lógica que describen los tipos de objetos de comando que puede manejar, y cómo hacer pasar los que no puede manejar a la siguiente objeto de procesamiento en la cadena. También existe un mecanismo para agregar nuevos objetos de procesamiento al final de esta cadena .

Esto encaja perfectamente con lo que necesita hacer.

+0

Voy a jugar con él esta tarde, ¡pero estoy bastante seguro de que esta es la respuesta que estaba buscando! – RobinHood70

+0

Cualquier cosa, solo dame un grito. – Aliostad

+0

¿Algún consejo sobre cómo implementar esto en términos de llamadas posteriores? Para poner mi caso específico en un ejemplo más genérico, supongamos que tengo un método de Cargar y Guardar, donde la Carga maneja archivos XML o TXT. Save no necesita "volver a calcularlo", Load ya ha determinado lo que está manejando. Estoy pensando que la clase principal solo necesita tener una propiedad de controlador que indique qué manejador finalmente manejó el evento, pero no estoy seguro de cuál es la mejor manera de implementarlo. – RobinHood70

0

creo que es necesario Task/Task<T>, especialmente ContinueWith(Action<Task>) método y 4 propiedades: IsCanceled, IsCompleted, IsFaulted y Result.

1

Probablemente usaría el patrón de estrategia aquí.

http://en.wikipedia.org/wiki/Strategy_pattern

sólo tendrá que pasar delgates en lugar de clases enteras. Si todos los métodos usan la misma información para el procesamiento, esto podría ser útil.

+0

Esta es también una buena solución, aunque si estoy entendiendo correctamente, no tendría el comportamiento fallido que estaba buscando. – RobinHood70

+0

Parece que tu patrón de estrategia está entrando en juego después de todo. :) ¡Gracias! – RobinHood70