Pregunta

Tengo una situación en la que intento mantener mi modelo e implementación lo más débilmente posible, sin embargo, me enfrento a una situación en la que el acoplamiento podría estar mucho más cerca de lo que quiero.

Tengo una selección de clases 'Modelo', todas implementando interfaces. Además, tengo clases de 'Acceso a datos', que proporcionan una serie de funciones, una de las cuales es decodificar valores de búsqueda de enteros en su representación completa de 'objeto'.

Dentro de mis clases de modelo, quiero proporcionar acceso a estos valores decodificados sin requerir que el modelo sepa sobre las clases de acceso a datos.

Un ejemplo simplificado es:

/// Core classes --

class Car : ICar
{
    public int MakeId { get {...} set { ... } }

    public IMakeInfo Make { get {...} }

    public string Registration { get { ... } set { ... } }

    public int CurrentOwnerId { get { ... } set { ... } }

    public IPerson CurrentOwner { get { ... } }
}

class MakeInfo : IMakeInfo
{
    public string Name { ... }
    public int Id { ... }
    public decimal Weight { ... }
    // etc etc
}

/// Data Access Classes --

class ResolveMake 
{
    public IMakeInfo GetMakeInfo(int id)
    { 
        // Implementation here...
    }

}

¿Cómo habilito la clase Car para proporcionar el objeto IMakeInfo a las clases consumidoras sin informarlo directamente de la clase ResolveMake? En la instancia real en la que estoy trabajando, la clase Car no está en el mismo espacio de nombres que la clase ResolveMake y no contiene referencias a ninguna instancia de la misma.

Algunas de mis opciones:

  • Implemente un delegado en Car que se puede suministrar con una instancia del método GetMakeInfo .
  • Algún tipo de inyección de dependencia
  • Acople estrechamente el automóvil a ResolveMake y termine con él.
  • ¿Alguna otra opción?

¡Cualquier sugerencia es bienvenida!

¿Fue útil?

Solución

Métodos de extensión ?

namespace CarStuff
{
   class Car : ICar
   {
      public int MakeId { get {...} set { ... } }
      // no Make property...
      public string Registration { get { ... } set { ... } }
      public int CurrentOwnerId { get { ... } set { ... } }
      public IPerson CurrentOwner { get { ... } }
   }
}


namespace MakeExts
{
   class ResolveMake
   {
      public static IMakeInfo Make(this Car myCar)
      {
         //implementation here
      }
   }
}

en otro lugar:

using MakeExts;

Car c = new Car();
Console.WriteLine(c.Make().ToString());

Editar: para usar los métodos de extensión en .NET 2.0, necesita algo como:

Básicamente, una clase que contiene:

namespace System.Runtime.CompilerServices 
{ 
   class ExtensionAttribute : Attribute
   {
   }
 }

y un " usando System.Runtime.CompilerServices " dispersos en lugares relevantes.

Otros consejos

A mí me parece una inyección de dependencia. He hecho cosas similares con MS PP Unity y la inyección de contructor, así como la inyección de métodos y propiedades. Entonces su clase de automóvil tendría algún tipo de inyección del IMakeInfo ... ejemplo:

[InjectionMethod]
public void Initialize([Dependency] IMakeInfo makeInfo)
{
  this.MakeInfo = makeInfo;
}

Dado que la clase Car tiene una propiedad Make que devuelve IMakeInfo, parece que ya proporciona la información. Entonces, ¿estoy en lo cierto al suponer que su problema es más cómo se le proporciona al automóvil el valor para devolver?

Si este es el caso, tal vez desee considerar crear un método de fábrica que conoce sobre autos y ResolveMake.

Siguiendo su analogía, el Coche obviamente necesita saber para cuando get_Make se llama qué es IMakeInfo. También consideraría que hacer una característica clave del automóvil. Así que creo que pasar un IMakeInfo al constructor de automóviles (posiblemente usando IOC) es muy razonable. Si esta es una buena analogía con su código real (cada "Coche" tiene un único, intrínseco, "IMakeInfo"), iría con esto.

También podría usar un setter como dijo Johan, nuevamente con IOC opcional. La razón por la que preferiría un constructor es que parece cada " Car " debe tener un " Hacer " ;.

Al final (dadas las limitaciones en las que estaba trabajando), en realidad usé una variación en varios de los temas anteriores.

Debido a que tenía que evitar cualquier referencia a mi capa de acceso a datos debido a referencias circulares, terminé haciendo algo con los delegados y el código de 'fábrica'. Ajustándolo en mi escenario original hice lo siguiente:

class Car 
{
    public void SetLookupProvider(ILookupProvider value) { _lookupProvider = value; }

    public IMakeInfo Make { get { return _lookupProvider.ResolveMake(MakeId); } }

    ....
}

interface ILookupProvider
{
    IMakeInfo ResolveMake(int id);
}

class LookupProvider
{
    public delegate IMakeInfo ResolveMakeDelegate(int id);

    public ResolveMakeDelegate ResolveMakeDel { set { _resolvemake = value; } }

    public IMakeInfo ResolveMake(int id){ return _resolvemake(id); }  
}

Luego, en mi método de fábrica ...

ICar returnedObject = new Car(blah, foo, bar, etc);

ILookupProvider luprovider = new LookupProvider();
luprovider.ResolveMakeDel = DataAccessLayer.FunctToGetMakeInfo;

(Car)returnedObject.SetLookupProvider(luprovider).

Ahora soy el primero en admitir que esta no es la solución más bonita (me hubiera ido con los métodos de extensión si hubiera accedido a un compilador 3.0 ...) pero mantiene la clase Car ligeramente acoplada a la capa DataAccess (que en mi situación impidió el infierno de referencia circular ...). La clase Car no necesita saber cómo está obteniendo el resultado y el método de fábrica que está produciendo los objetos Car en primer lugar es lo único que está acoplado a la capa de acceso a Datos.

Todavía no he marcado una respuesta, dejaré que algunas personas voten y luego obtengan la más alta, especialmente porque creo que todas son respuestas válidas (es solo que no pude usar ninguna en su totalidad en esta instancia).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top