【问题标题】:Any suitable patterns for this problem?有适合这个问题的模式吗?
【发布时间】:2009-06-22 08:23:45
【问题描述】:

我有一种情况,我试图让我的模型和实现尽可能松散耦合,但是我面临的情况是耦合可能比我想要的更接近。

我有一个“模型”类的选择,所有实现接口。此外,我还有“数据访问”类,它们提供了许多函数,其中之一是将整数查找值解码为其完整的“对象”表示。

在我的模型类中,我想提供对这些解码值的访问,而不需要模型知道数据访问类。

一个简化的例子是:

/// 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 紧密耦合并完成。
  • 还有其他选择吗?

欢迎提出任何建议!

【问题讨论】:

  • 您能否以某种方式将ResolveMake 实例传递给Car,让它修改该实例并通过调用Make 将其返回?
  • 您在模型中使用接口是否有原因?这通常被认为是一种反模式,例如lostechies.com/blogs/jagregory/archive/2009/05/09/…
  • @BengtBe - 虽然我同意这确实有反模式的味道,但我给出的示例纯粹是这样,实际情况充分利用了接口和由此产生的抽象。
  • @mikem - 这就是我对委托的想法,(可能是一个接受一个实现的属性,它接受一个 int 并返回一个 IMakeInfo 对象)当然,那么你仍然有实际提供该委托的问题对所有 Car 实例起作用..
  • 您可以使用针对 .NET2.0 的 C#3.0 编译器吗?在这种情况下,我的编辑应该允许您使用扩展方法...

标签: c# design-patterns .net-2.0 loose-coupling


【解决方案1】:

Extension Methods?

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”散布在相关的地方。

【讨论】:

  • 啊不幸的是,在这种情况下,我使用的是 .net 2.0 - 我将重新标记问题。
【解决方案2】:

对我来说,这听起来像是依赖注入。我用 MS PP Unity 和构造函数注入以及方法和属性注入做了类似的事情。 然后你的 Car 类将有某种 IMakeInfo 注入...例如:

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

【讨论】:

    【解决方案3】:

    由于 Car 类具有返回 IMakeInfo 的 Make 属性,因此看起来它已经提供了信息。那么我是否正确地假设您的问题更多是如何为汽车提供返回值?

    如果是这种情况,也许您想创建一个了解汽车和 ResolveMake 的 factory method

    【讨论】:

      【解决方案4】:

      按照您的类比,Car 显然需要在 get_Make 被调用时知道 IMakeInfo 是什么。我也会考虑使汽车的一个关键特征。所以我认为将 IMakeInfo 传递给 Car 构造函数(可能使用 IOC)是非常合理的。如果这是与您的真实代码的一个很好的类比(每个“汽车”都有一个内在的“IMakeInfo”),我会选择这个。

      您也可以使用 Johan 所说的 setter,同样可以使用可选的 IOC。我更喜欢构造函数的原因是,似乎每个“汽车”都应该有一个“制造”。

      【讨论】:

        【解决方案5】:

        最后(考虑到我工作的限制)我实际上使用了上述许多主题的变体。

        由于发生循环引用,我不得不避免对我的数据访问层的任何引用,我最终使用委托和“工厂”代码做一些事情。将其放入我的原始场景中,我做了以下操作:

        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 对象的工厂方法是唯一与数据访问层耦合的东西。

        我还没有标记答案,我会让更多的人投票,然后选择最高的 - 特别是因为我认为它们都是有效的答案(只是我不能完全使用任何一个这个实例)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-09-04
          • 1970-01-01
          • 2012-07-28
          • 1970-01-01
          • 2021-05-08
          • 1970-01-01
          • 2012-04-25
          • 2020-04-15
          相关资源
          最近更新 更多