2012-04-04 16 views
7

responsabilidad: Me encantaría estar usando dependency injection en este proyecto y tienen un diseño basado en la interfaz de acoplamiento flexible en todos los ámbitos, pero el uso de la dependencia de la inyección ha sido derribado en este proyecto. También los principios de diseño SOLID (y design patterns en general) son algo extraño donde trabajo y soy nuevo en muchos de ellos. Así que tome eso en cuenta al sugerir un mejor diseño para este problema.¿Hay una mejor opción de diseño?

Aquí hay una versión simplificada del código en el que estoy trabajando, y como tal podría parecer artificial. Si es así, me disculpo. Tenga en cuenta las siguientes clases:

// Foo is a class that wraps underlying functionality from another 
// assembly to create a simplified API. Think of this as a service layer class, 
// a facade-like wrapper. It contains a helper class that is specific to 
// foo. Other AbstractFoo implementations have their own helpers. 

public class Foo : AbstractFoo 
{ 
    private readonly DefaultHelper helper; 
    public override DefaultHelper Helper { get { return helper; } } 

    public Foo() 
    { 
     helper = new Helper("custom stuff"); 
    } 

    public override void Operation1(string value) 
    { 
     Console.WriteLine("Operation1 using " + value); 
    } 

    public override void Operation2() 
    { 
     Console.WriteLine("Operation2"); 
    } 
} 

// Helper derives from a default implementation and allows us to 
// override it's methods to do things specific for the class that 
// holds this helper. Sometimes we use a custom helper, sometimes 
// we use the default one. 

public class Helper : DefaultHelper 
{ 
    private readonly string customStuff; 

    public Helper(string value) 
    { 
     customStuff = value; 
    } 

    public override void DoSomethingHelpful() 
    { 
     Console.WriteLine("I was helpful using " + customStuff); 
    } 
} 

decir estas dos clases se utilizan como sigue:

// foo referenced and used in one part of code 
    var foo = new Foo(); 
    foo.Operation2(); // or foo.Operation1(); 

    // some other point in the program where we don't have a reference to foo 
    // but do have a reference to the helper 
    helper.DoSomethingHelpful(); 

Sin embargo ahora encuentro que también necesito para realizar foo.Operation1 en algunas implementaciones de helper.DoSomethingHelpful();? Las posibles soluciones alternativas que pensé serían:

  1. Have foo and helper tienen una relación bidireccional. De modo que en DoSomethingHelpful podemos llamar foo.Operation2
  2. Ha foo implementar la interfaz iHelp y mover el código de "ayudante" en foo
  3. Uso delegación y aprobar el método de funcionamiento 2 como delegado Action<string> en el constructor del ayudante.

Ninguno de estos enfoques parece ser ideal (aunque he más o menos decidido que no me gusta la opción 1 y estoy preocupado por mantenimiento con la opción 3 si descubrimos más tarde que necesitamos para pasar en más delegados). Esto me hace preguntarme si hay un problema con el diseño inicial del combo Helper/Foo. ¿Pensamientos?

+0

Creo que quiere decir 'DefaultHelper' en lugar de' DefualtHelper'. – ja72

+0

@ ja72, la ortografía nunca fue mi fuerte, he actualizado este error tipográfico. – Matt

+1

+1 por la exención de responsabilidad ... Se rió mucho. – scottheckel

Respuesta

1

"Ninguno de estos enfoques parecen ser ideal (aunque no tengo más o menos decidido que no me gusta la opción 1 y estoy preocupado por la mantenibilidad con la opción 3 si descubrimos más tarde tenemos que pasar en más delegados). Esto me hace preguntarme si hay un problema con el diseño inicial de del combo Helper/Foo. "

Tienes razón: hay un problema con el diseño de Helper y Foo. La relación básica de Foo/Ayudante como la describiste inicialmente está bien, y es un patrón común cuando tienes que envolver otros objetos que no controlas. Pero luego dices:

"¿Qué pasa si me entero de que yo también necesito para realizar foo.Operation1 en algunas implementaciones de helper.DoSomethingHelpful() ;?"

Aquí es donde tenemos un problema. Empezaste describiendo una relación en la que Foo depende de Helper; ahora estás describiendo una relación donde Helper depende de Foo. Eso inmediatamente me dice que sus relaciones de dependencia están enredadas. Las relaciones de dependencia entre los objetos solo deben ir en una dirección; de hecho, la inyección de dependencia se basa en esto.

+0

+1 Acepto aquí y gracias por identificar por qué no me gustan las opciones 1 y 3 (es decir, dependencias enredadas). Y por qué creo que la combinación de la funcionalidad de Foo y Helper en este caso tiene sentido debido a su interdependencia. Usando la interfaz IHelp, puedo tratar a Foo como si fuera un Helper en otro lugar del código. – Matt

1

Creo que tiene lo que necesita. Intente no diseñar para el "por si acaso lo necesito más tarde" y no arregle lo que no está roto. Si en el futuro necesita usar Operation1 de su helper, agréguelo como una dependencia del constructor (como sugirió), o simplemente páselo al método al que está llamando. Dependerá del escenario, y lo tendrás cuando realmente necesites algo.

EDIT: cambió la frase "Intente no diseñar para el futuro", ya que no parece lo que quiero decir.cambios

nuevo EDIT debido en la cuestión

Se podía así que algo como esto:

helper.DoSomethingUsefulWith(foo); 

por lo que su método de ayuda recibirá la dependencia que necesita con el fin de trabajar

+0

¿Intenta no diseñar para el futuro? Entiendo lo que intentas decir, pero en general no creo que sea un buen consejo. –

+0

Diseñe para que pueda mantenerse y extenderse: ¡SÍ! Agregar funcionalidad solo en caso de que se necesite más tarde: ¡NO! eso es lo que intento decir :). Creo que no elegí las mejores palabras para él jeje – ivowiblo

+0

@ivowiblo, actualicé mi pregunta porque no es realmente una situación de "y si". Este es un requisito que ahora me he encontrado. Debería haber sido más claro. Lo siento. – Matt

3

Cómo sobre una relación casual ("usos"):

public class Helper : DefaultHelper 
{ 
    private readonly string customStuff; 

    public Helper(string value) 
    { 
     customStuff = value; 
    } 

    public override void DoSomethingHelpful(AbstractFoo foo) 
    { 
     foo.Operation1(); 
     Console.WriteLine("I was helpful using " + customStuff);   
    } 
} 

Por lo tanto, debe modificar el Ayudante abstracto para esperar una referencia a la implementación correcta de Foo.

+1

En UML, esta es la [Dependencia] (http://publib.boulder.ibm.com/infocenter/rtnlhelp/v6r0m0/index.jsp?topic=%2Fcom.ibm.xtools.modeler.doc%2Ftopics%2Fcdepend. html) o "usos" de la relación – SwDevMan81

+0

El problema con esto es que Helper tiene las mismas firmas que usa AbstractFoo y otras clases (es decir, AbstractBoo). Si cambio la clase base para aceptar foo, esto no es útil cuando uso un helper de boo, si tiene sentido. Es posible que tenga que pasar Boo en su lugar (o podría estar bien sin parámetros). – Matt

+0

@Matt: Bueno, ¿quieres que 'BooHelper' tenga acceso a' Boo' de la misma manera que quieres que 'FooHelper' tenga acceso a' Foo', o no? Tu comentario realmente me confundió. – ja72

1

Creo que todas sus soluciones son buenas; solo ofrecen diferentes capacidades. Estas diferencias ya no importan demasiado, pero es probable que así sea en el futuro.

Prefieres la segunda, y tus instintos son la mejor guía aquí, ya que sabes más que el resto de nosotros sobre el futuro de tu código. Me gusta su segunda solución lo mejor solo porque se deshace de una clase y es más simple. Debido a su simplicidad, si tiene que hacer algo más tarde, no tendrá que perder mucho trabajo.

El primer método te permite jugar juegos con diferentes instancias y subclases de Helper (IHelper?). El último método agrega mucha flexibilidad a Helper. (Aunque puede agregar tanto que no necesita ayuda, solo el método que le está pasando). Puede cambiar a usarlos más adelante si alguno de ellos parece resolver más problemas futuros no analizados.

+0

+1 Acepto el enfoque de interfaz y la combinación de Foo y Helper probablemente sea la mejor de las tres y es con la que decidí ir. – Matt

Cuestiones relacionadas