我有一种情况,我试图让我的模型和实现尽可能松散耦合,但是我遇到的情况是耦合可能比我想要的更接近。
我有一些'Model'类,所有实现接口。此外,我还有“数据访问”类,它提供了许多功能,其中一个功能是将整数查找值解码为完整的“对象”表示。
在我的模型类中,我想提供对这些解码值的访问,而不需要模型知道数据访问类。
一个简单的例子是:
/// 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...
}
}
如何让Car
类为任何消费类提供IMakeInfo
对象而不直接让它知道ResolveMake类?在实际的实例中,我使用Car类与ResolveMake类不在同一名称空间中,并且它不包含对它的任何实例的引用。
我的一些选择:
Car
中实施一个委托,该委托可以提供GetMakeInfo
方法的实例。欢迎任何建议!
答案 0 :(得分:1)
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
}
}
}
别处:
using MakeExts;
Car c = new Car();
Console.WriteLine(c.Make().ToString());
编辑:要在.NET 2.0中使用扩展方法,您需要以下内容:
基本上是一个包含以下内容的类:
namespace System.Runtime.CompilerServices
{
class ExtensionAttribute : Attribute
{
}
}
和“使用System.Runtime.CompilerServices”分散在相关的地方。
答案 1 :(得分:1)
这对我来说听起来像依赖注入。我已经用MS PP Unity做了类似的事情,同时也进行了构造函数注入以及方法和属性注入。 然后你的Car类会有一些注入IMakeInfo ......例如:
[InjectionMethod]
public void Initialize([Dependency] IMakeInfo makeInfo)
{
this.MakeInfo = makeInfo;
}
答案 2 :(得分:0)
由于Car类具有返回IMakeInfo的属性Make,因此它看起来已经提供了信息。所以我正确地假设您的问题更多的是如何为汽车提供返回值?
如果是这种情况,或许你想看一下创建一个了解汽车和ResolveMake的factory method。
答案 3 :(得分:0)
按照你的类比,汽车显然需要知道get_Make被称为IMakeInfo的时间。我也会认为这是汽车的关键特征。所以我认为将IMakeInfo传递给Car构造函数(可能使用IOC)是非常合理的。如果这是对你真实代码的一个很好的比喻(每个“Car”都有一个内在的“IMakeInfo”),我会选择这个。
你也可以使用像Johan这样的二传手,再次使用可选的IOC。我更喜欢构造函数的原因是它似乎每个“Car”应该有一个“Make”。
答案 4 :(得分:0)
最后(考虑到我在其中工作的限制),我实际上使用了上述一些主题的变体。
由于循环引用的出现,我不得不避免对我的数据访问层的任何引用,因此我最终对代理和“工厂”代码做了一些事情。根据我的原始场景,我做了以下几点:
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); }
}
然后在我的工厂方法......
ICar returnedObject = new Car(blah, foo, bar, etc);
ILookupProvider luprovider = new LookupProvider();
luprovider.ResolveMakeDel = DataAccessLayer.FunctToGetMakeInfo;
(Car)returnedObject.SetLookupProvider(luprovider).
现在我是第一个承认这不是最漂亮的解决方案(如果我访问3.0编译器,我会使用扩展方法)但它确实使Car类松散地耦合到DataAccess层(在我的情况下阻止循环引用地狱......)。 Car类不需要知道它是如何获得结果的,并且首先生成Car对象的工厂方法是唯一耦合到数据访问层的方法。
我还没有给出答案,我会让更多的人投票然后选择最高的 - 特别是因为我认为他们都是有效的答案(只是我不能完全使用任何一个)这个例子)。