Quaisquer padrões adequados para este problema?
-
06-07-2019 - |
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étodoGetMakeInfo
. - 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!
Soluçã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:
- http://kohari.org/2008/ 04/04 / extensão-methods-in-net-20 /
- http: // www .danielmoth.com / Blog / 2007/05 / utilizando-extension-methods-em-fx-20.html
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).