【问题标题】:Does abstract factory introduce tight coupling抽象工厂是否引入了紧耦合
【发布时间】:2016-04-07 19:16:44
【问题描述】:

我有一个场景,我认为我没有严格地对接口进行编码。 假设我有一个接口(在某些程序集中)

public interface IFoo
{
    void GetSomething();
    void SaveSomething();
}

现在,我有一个实现(在其他程序集中)

public class FoodB : IFoo
{
   public FoodB(Guid guid, IFoodBConfig config)  //IFoodBConfig is defined in same library where FoodB exists
   {
      ...
   }
}

所以,基本上实现需要guid和config

现在,我有一个客户(在其他程序集中)希望使用 IFoo,类似于

public class Client
{
    public Client(IFoo foo)
    {
       ...
    }
}

DIP 表示必须在组合根中提供实现,并且客户端不得实例化对象本身。不错不错。

现在,组合根或 IoC 容器决定注入 FoodB 作为具体实例,但它没有 'guid' 的值,因为它仅在运行时可用。

所以,我们决定使用抽象工厂。

public abstract FooFactory
{
    public abstract IFoo GetFoo(Guid guid, IFoodBConfig config);
}

现在,提出了几个问题

a) 客户端构造函数必须修改为接受FooFactory 而不是IFoo,这意味着客户端已经决定使用什么实现。在我看来,这使得软件紧密耦合

b) 即使客户端构造函数被修改为接受FooFactoryIFoodBConfig 混凝土也必须提供给构造函数,这将使客户端引用存在FoodB 的库。

c) 工厂声明和实现将放在哪里?将FooFactory 放在声明IFoo 的库中是不正确的,因为工厂与IFoo 无关。由于同样的原因,将FooFactory 放入FoodB 库中也不正确

d) 简而言之,使用抽象工厂意味着交换实现的灵活性已经消失,因为客户端是绑定到 FoodBFactory 的目录。

这种方法是否不正确,或者这是我们必须忍受的一些限制?

【问题讨论】:

  • a) 抽象类不能被实例化,它不是一个实现。你只耦合到抽象类的接口。 b) 如果你想使用某些东西,你必须引用它的库 c) 工厂创建 IFoo,但与 IFoo 无关? d) 不,它与抽象的 FooFactory 相关联
  • 这是否意味着FooFactory 本质上应该重命名为公共接口FoodBFactory 以明确声明它只创建FoodB 类型?

标签: oop design-patterns factory factory-pattern abstract-factory


【解决方案1】:

这个问题需要解决的几个问题。

  1. FooFactory 不是 Abstract Factory 的示例,至少在 GoF 术语中如此。
    Abstract Factory != 名为 Factory 的抽象类。
  2. 组合根不需要在编译时具有其所有参数。现代 IoC 容器都应该支持运行时参数。

a) 正如@dbugger 所评论的,将客户端耦合到抽象类与耦合到接口没有什么不同。

b) FooBFactory 实现或 IoC 容器都可以预先配置为 FooBConfig。然后客户端只传递其运行时 guid。

c) FooFactoryIFoo 直接相关,因为它的目的是返回IFoo 的实例。确实,它的方法的返回类型是IFoo

d) 之所以存在灵活性,是因为 IoC 容器可以将 Factory Method 的不同实现注入客户端,允许客户端在不了解产品实现或用于实例化它的工厂实现的情况下获取不同的产品。

如果您愿意将客户端耦合到 IoC 容器,则客户端可以直接将容器用作工厂,从而消除额外的工厂接口。

【讨论】:

  • 好吧,首先客户端只需要IFoo实例作为依赖。那么问题是为什么工厂会出现在画面中?只是因为客户想要FoodB 实例传递给它,它决定它需要一个工厂。对我来说,这本身就是与实现的耦合。在最纯粹的方法中,客户端必须简单地通告依赖项(即IFoo)并让组合根注入它们。
  • 另外,我现在可以将 IFoo 的不同实现传递给 Client 吗?当然不是。我将不得不通过 FooFactory 的工厂实现,并且可能是该实现不需要 guid 或配置
  • 这个客户不仅需要IFoo。它需要一些可以获取客户指导并返回IFoo 的东西。如果客户端不需要传递 guid,那么它可能只需要一个IFoo。无论哪种方式,客户的需求决定了依赖的性质。
  • 工厂方法是解决这个问题的好方法,特别是如果config 可以移动到具体工厂中,这样客户就不必处理它了。
猜你喜欢
  • 2014-12-10
  • 1970-01-01
  • 2017-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-28
  • 1970-01-01
  • 2013-12-25
相关资源
最近更新 更多