对于这个问题有什么合适的模式吗?
-
06-07-2019 - |
题
我遇到了一种情况,我试图使我的模型和实现尽可能松散地耦合,但是我面临着一种情况,耦合可能会比我想要的更紧密。
我选择了“模型”类,所有类都实现了接口。此外,我还有“数据访问”类,它提供了许多功能,其中之一是将整数查找值解码为其完整的“对象”表示形式。
在我的模型类中,我希望提供对这些解码值的访问,而不要求模型了解数据访问类。
一个简化的例子是:
/// 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
方法。 - 某种依赖注入
- 将 Car 与 ResolveMake 紧密耦合并完成。
- 还有其他选择吗?
欢迎任何建议!
解决方案
扩展方法?
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 中使用扩展方法,您需要类似以下内容:
- http://kohari.org/2008/04/04/extension-methods-in-net-20/
- http://www.danielmoth.com/Blog/2007/05/using-extension-methods-in-fx-20.html
基本上,一个类包含:
namespace System.Runtime.CompilerServices
{
class ExtensionAttribute : Attribute
{
}
}
以及分散在相关地方的“使用 System.Runtime.CompilerServices”。
其他提示
对我来说这听起来像是依赖注入。我已经使用 MS PP Unity 以及构造函数注入以及方法和属性注入完成了类似的事情。然后你的 Car 类将会有某种形式的 IMakeInfo 注入...示例:
[InjectionMethod]
public void Initialize([Dependency] IMakeInfo makeInfo)
{
this.MakeInfo = makeInfo;
}
由于 Car 类有一个返回 IMakeInfo 的属性 Make,看起来它已经提供了信息。那么我的假设是否正确,您的问题更多是如何为汽车提供返回的价值?
如果是这种情况,也许您想考虑创建一个 工厂方法 它了解汽车和 ResolveMake。
按照你的类比,汽车显然需要在调用 get_Make 时知道 IMakeInfo 是什么。我还认为品牌是汽车的一个关键特征。所以我认为将 IMakeInfo 传递给 Car 构造函数(可能使用 IOC)是非常合理的。如果这与您的真实代码有很好的类比(每个“Car”都有一个内在的“IMakeInfo”),那么我会同意这一点。
你也可以使用像 Johan 所说的 setter,同样带有可选的 IOC。我更喜欢构造函数的原因是它似乎 每一个 “汽车”应该有一个“品牌”。
最后(考虑到我工作的限制)我实际上使用了上述许多主题的变体。
因为我必须避免由于发生循环引用而对我的数据访问层进行任何引用,所以我最终使用委托和“工厂”代码做了一些事情。将其融入我原来的场景中,我做了以下事情:
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 对象的工厂方法是唯一耦合到数据访问层的东西。
我还没有标记答案,我会让更多的人投票,然后选择最高的 - 特别是因为我认为它们都是有效的答案(只是在这种情况下我不能完整使用任何一个) 。