抽象工厂是否引入紧耦合
Does abstract factory introduce tight coupling
我有一个场景,我认为我没有严格按照接口进行编码。
假设我有一个接口(在某些程序集中)
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) 即使客户端构造函数被修改为接受 FooFactory
,也必须向构造函数提供 IFoodBConfig
混凝土,这将使客户端引用 FoodB
所在的库存在。
c) 工厂声明和实现会放在哪里?将 FooFactory
放在声明 IFoo
的库中是不正确的,因为工厂与 IFoo
无关。由于同样的原因,将 FooFactory
放入 FoodB
库中也不正确
d) 简而言之,使用抽象工厂意味着交换实现的灵活性已经消失,因为客户端是绑定到 FoodBFactory
.
的目录
方法是否不正确,或者这是我们必须忍受的一些限制?
这个问题有几点要解决。
FooFactory
不是 Abstract Factory
的示例,至少按照 GoF 术语来说是这样。
Abstract Factory
!= abstract class named Factory。
- 组合根不需要在编译时拥有它的所有参数。现代 IoC 容器应该都支持运行时参数。
a) 正如@dbugger 评论的那样,将客户端耦合到抽象 class 与耦合到接口没有什么不同。
b) FooBFactory
实现或 IoC 容器都可以使用 FooBConfig
进行预配置。然后客户端仅传递其运行时 guid。
c) FooFactory
与 IFoo
直接相关,因为它的目的是 return IFoo
的实例。事实上,其方法的 return 类型是 IFoo
.
d) 存在灵活性是因为 IoC 容器可以将 Factory Method 的不同实现注入客户端,允许客户端在不知道产品实现或用于实例化的工厂实现的情况下获取不同的产品它。
如果您愿意将客户端耦合到 IoC 容器,客户端可以直接将容器用作工厂,从而消除额外的工厂接口。
我有一个场景,我认为我没有严格按照接口进行编码。 假设我有一个接口(在某些程序集中)
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) 即使客户端构造函数被修改为接受 FooFactory
,也必须向构造函数提供 IFoodBConfig
混凝土,这将使客户端引用 FoodB
所在的库存在。
c) 工厂声明和实现会放在哪里?将 FooFactory
放在声明 IFoo
的库中是不正确的,因为工厂与 IFoo
无关。由于同样的原因,将 FooFactory
放入 FoodB
库中也不正确
d) 简而言之,使用抽象工厂意味着交换实现的灵活性已经消失,因为客户端是绑定到 FoodBFactory
.
方法是否不正确,或者这是我们必须忍受的一些限制?
这个问题有几点要解决。
FooFactory
不是Abstract Factory
的示例,至少按照 GoF 术语来说是这样。
Abstract Factory
!= abstract class named Factory。- 组合根不需要在编译时拥有它的所有参数。现代 IoC 容器应该都支持运行时参数。
a) 正如@dbugger 评论的那样,将客户端耦合到抽象 class 与耦合到接口没有什么不同。
b) FooBFactory
实现或 IoC 容器都可以使用 FooBConfig
进行预配置。然后客户端仅传递其运行时 guid。
c) FooFactory
与 IFoo
直接相关,因为它的目的是 return IFoo
的实例。事实上,其方法的 return 类型是 IFoo
.
d) 存在灵活性是因为 IoC 容器可以将 Factory Method 的不同实现注入客户端,允许客户端在不知道产品实现或用于实例化的工厂实现的情况下获取不同的产品它。
如果您愿意将客户端耦合到 IoC 容器,客户端可以直接将容器用作工厂,从而消除额外的工厂接口。