Pergunta

Eu tenho uma situação em que estou tentando manter meu modelo e implementação como de baixo acoplamento possível, porém eu sou confrontado com uma situação em que o acoplamento poderia obter muito mais perto do que eu quero.

Eu tenho uma seleção de aulas de 'modelo', todas as interfaces de execução. Além disso eu tenho aulas 'de acesso a dados', que fornecem um número de funções, um dos quais está a descodificar valores de pesquisa inteiro em sua representação completa 'objeto'.

Dentro de minhas classes de modelo que deseja fornecer acesso a esses valores decodificados sem exigir que o modelo de saber sobre as classes de acesso de dados.

Um exemplo simplificado é:

/// 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...
    }

}

Como faço para ativar a classe Car para fornecer o objeto IMakeInfo para todas as classes de consumo sem fazer directamente-lo ciente da classe ResolveMake? Na instância real Eu estou trabalhando com a classe de carro não é no mesmo namespace como a classe ResolveMake e não contém referências a quaisquer instâncias do mesmo.

Alguns dos meus opções:

  • Implementar um delegado no Car que pode ser fornecido com um exemplo do método GetMakeInfo.
  • Algum tipo de injeção de dependência
  • Intimamente casal Car para ResolveMake e ser feito com ele.
  • Todas as outras opções?

Todas as sugestões bem-vindos!

Foi útil?

Solução

Métodos de extensão ?

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
      }
   }
}

em outro lugar:

using MakeExts;

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

Edit: Para usar métodos de extensão no .NET 2.0, você precisa de algo como:

Basicamente, uma classe que contém:

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

e um "usando System.Runtime.CompilerServices" espalhados em lugares relevantes.

Outras dicas

Parece que Dependency Injection para mim. Eu tenho feito coisas semelhantes com MS PP Unidade e a injeção contructor, bem como método e propriedade injeção. Em seguida, a sua classe carro teria algum tipo de injeção da IMakeInfo ... exemplo:

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

Uma vez que a classe Car tem uma propriedade Marca que retorna IMakeInfo parece que ele já fornece as informações. Então estou certo em assumir que o seu problema é mais como o carro é fornecido com o valor de retorno?

Se este for o caso, talvez você quer olhar para a criação de um fábrica método que sabe sobre carros e ResolveMake.

Ir com a analogia, o carro, obviamente, precisa saber pelo tempo get_Make é chamado o que o IMakeInfo é. Gostaria também de considerar a marca uma característica chave do carro. Então eu acho que a passagem de um IMakeInfo para o construtor do carro (possivelmente usando COI) é muito razoável. Se esta é uma boa analogia ao seu código real (cada "Carro" tem um único, intrínseco, "IMakeInfo"), eu iria com isso.

Você também pode usar um setter como Johan disse, novamente com o COI opcional. A razão que eu preferiria um construtor é que parece todas "Car" deve ter uma "Marca".

Na extremidade (tendo em conta os constrangimentos eu estava a trabalhar dentro) que efectivamente utilizada uma variação de um número dos temas acima.

Porque eu tinha que evitar quaisquer referências a minha camada de acesso a dados devido a referências circulares que ocorrem acabei fazendo algo com delegados e o código 'fábrica'. Encaixando-a no meu cenário original eu fiz o seguinte:

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); }  
}

Em seguida, no meu 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).

Agora eu sou o primeiro a admitir que esta não é a solução mais bonita (eu teria ido com métodos de extensão se eu tivesse acesso a um compilador 3.0 ..), mas ele faz manter a classe Car fracamente acoplada à camada DataAccess (que na minha situação impediu inferno referência circular ...). A classe de carro não precisa saber como ele está ficando o resultado e o método de fábrica que está produzindo objetos do carro em primeiro lugar é a única coisa que é acoplado à camada de acesso a dados.

Eu não tenho marcada uma resposta ainda, eu vou deixar um pouco mais voto as pessoas, em seguida, ir com o mais alto - particularmente como eu acho que eles são todas as respostas válidas (seu justo que eu não poderia usar qualquer um na totalidade em neste caso).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top