这是抽象工厂模式的正确或可行的实现吗?
本文关键字:实现 抽象 工厂 模式 | 更新日期: 2023-09-27 18:29:50
灵感来自Mark Seemann的文章:模式识别:抽象工厂还是服务定位器?
我希望像这样写一个抽象工厂:
public interface IAbstractFactory {
T Create<T>();
}
然后,使用 Ninject 绑定它,如下所示:
IKernel kernel = new StandardKernel();
kernel.Bind<IAbstractFactory>().ToFactory();
然后,可以按如下方式使用它:
public class CustomerServiceIndicatorsModel {
public CustomerServiceIndicatorsModel(IAbstractFactory factory) {
this.emailIndicatorA = factory.Create<EmailIndicatorA>();
this.emailIndicatorB = factory.Create<EmailIndicatorB>();
this.emailIndicatorC = factory.Create<EmailIndicatorC>();
}
}
同样,我没有在任何地方引用 Ninject 的内核,这很有效。内核仅在绑定的Global.asax.cs
文件中引用。
是否可以将其视为抽象工厂模式的可接受实现?
我很难理解这种模式。我清楚地将Factory
模式理解为创建给定类型实例的委托类。
public interface IEmailIndicatorAFactory {
EmailIndicatorA Create();
}
然后,当我使用 Ninject ToFactory()
绑定它时,它将创建EmailIndicatorA
实例。
如何使用 Ninject 绑定IAbstractFactory<T>
,因为每种类型:
IAbstractFactory<EmailIndicatorA>
IAbstractFactory<EmailIndicatorB>
IAbstractFactory<EmailIndicatorC>
被认为是完全具体的类型。而且我找不到使用 Ninject
绑定它的方法.
而且我认为编写这样的接口没有任何好处,如果作为回报,我必须编写:
public interface EmailIndicatorAFactory : IAbstractFactory<EmailIndicatorA> { }
public interface EmailIndicatorBFactory : IAbstractFactory<EmailIndicatorB> { }
public interface EmailIndicatorCFactory : IAbstractFactory<EmailIndicatorC> { }
在 @PrestonGuillot 发表评论后,我落入了后者实现 ServiceLocator
而不是 AbstractFactory
,因为我使用的是通用Create<T>()
方法,而Abstract Factory
使用非泛型Create()
方法。
感谢您指出,@PrestonGuillot! =(
也许我在这里把事情复杂化了...这是我的模型:
电子邮件指示器
public abstract EmailIndicator {
int EmailCount { get; set; }
DateTime OldestDateTimeReceived { get; set; }
}
电子邮件指示器A
public class EmailIndicatorA : EmailIndicator { }
电子邮件指示器B
public class EmailIndicatorB : EmailIndicator { }
电子邮件指示器C
public class EmailIndicatorC : EmailIndicator { }
IEmailIndicatorRepository
public interface IEmailIndicatorRepository<T> where T : EmailIndicator {
T GetIndicator();
}
public class EmailIndicatorARepository : IEmailIndicatorRepository<EmailIndicatorA> {
public EmailIndicatorARepository(IExchangeService service
, IExchangeConfiguration configuration
, INetworkCredentialFactory credentialFactory) {
exchangeService = service;
exchangeService.Url = configuration.ExchangeUri;
exchangeService = credentialFactory.Create(configuration.Login, configuration.Password);
}
EmailIndicatorA GetIndicator() {
// Code to query Exchange through its Web services here...
}
}
还有另外两个类似的存储库,因为我必须在我的应用程序中查询三个不同的 Exchange 服务器。
我相信有使用Abstract Factory
的空间,而且因为我仍在学习模式,所以我还不知道如何在我的情况下实现它。
如果不是因为指标本身,也许我终于可以通过我的回购掌握Abstract Factory
。
就我的理解而言,以下是我将尝试作为解决方案的第一步:
工业工厂
public interface IRepositoryFactory<T> where T : class, new() {
T Create();
}
IEmailIndicatorARepositoryFactory
public interface IEmailIndicatorARepositoryFactory
: IRepositoryFactory<EmailIndicatorARepository> {
EmailIndicatorARepository CreateEmailIndicatorARepository();
}
IEmailIndicatorB实体工厂
public interface IEmailIndicatorBRepositoryFactory
: IRepositoryFactory<EmailIndicatorBRepository> {
EmailIndicatorBRepository CreateEmailIndicatorBRepository();
}
抽象工厂呢?
public abstract IEmailIndicatorRepositoryFactory
: IEmailIndicatorARepositoryFactory
, IEmailIndicatorBRepositoryFactory
, IEmailIndicatorCRepositoryFactory {
EmailIndicatorARepository CreateEmailIndicatorARepository() { // create instance here... }
EmailIndicatorBRepository CreateEmailIndicatorBRepository() { // create instance here... }
EmailIndicatorCRepository CreateEmailIndicatorCRepository() { // create instance here... }
}
这是更好的方法吗?
我在这里有点迷茫和困惑。
我发现维基百科文章的图表很有启发性。
我认为"抽象工厂"这个名字在.net/c#世界中有点误导。你只需要一个abstract class
以防语言不支持接口或你想共享实现(但请记住,支持组合而不是继承(。
既然你提到了Mark Seeman,我想提炼一下我对他关于IoC和Factory的一些帖子的解释:
- 不使用服务定位器
- 隐藏依赖项
- 接口隔离原理
- 创建合成根。理想情况下,它应该在开始时一次性实例化所有对象。
- 因此,您应该将对象设计为无状态,并且尽可能只处理数据(数据和处理逻辑的分离(。这意味着您只需要实例化或多或少的数据类/数据结构,它们没有任何依赖项。所有具有依赖项的对象都是在应用程序启动时一次性创建的。
- 晚创不会很快失败
- 如果使用延迟创建,无论将其称为服务定位器还是工厂,这都会降低可测试性,因为问题在应用程序启动时不可见,而仅在以后使用/创建对象时可见。
- 为了在一定程度上解决这个问题,请创建由工厂创建的对象的@composition依赖项 根目录。仅后期创建实际对象。
- 这是使用抽象工厂模式完成的。
因此,给定您的示例,这将是:
public interface IEmailIndicatorFactory
{
IEmailIndicator Create();
}
具有三个实现,一个用于EmailIndicatorA
,EmailIndicatorB
和EmailIndicatorC
。现在为了使论证有意义,EmailIndicator
需要一个特定于EmailIndicatorA
、(或 B, C(的依赖关系。假设它需要IServiceA
:
internal class EmailIndicatorA : IEmailIndicator
{
private readonly IServiceA serviceA;
public EmailIndicatorA(IServiceA serviceA)
{
this.serviceA = serviceA;
}
(...)
}
现在应该有一个相应的工厂来接收EmailIndicatorA
的依赖项:
internal class EmailIndicatorAFactory : IEmailIndicatorFactory
{
private readonly IServiceA serviceA;
public EmailIndicatorAFactory(IServiceA serviceA)
{
this.serviceA = serviceA;
}
public IEmailIndicator Create()
{
return new EmailIndicatorA(this.serviceA);
}
}
就是这样。如果EmailIndicatorA
有额外的依赖关系,它们也应该注入到EmailIndicatorAFactory
中。
现在,执行所有这些代码的好处是快速失败。所有其他问题都可以通过正确使用.ToFactory()
绑定和接口来解决,这些绑定和接口不是太通用而是特定的(基本上与上面的抽象工厂相同!
我认为你应该仔细权衡利弊。
Mark还表示,他很少再使用IoC容器了。相反,他通常更喜欢穷人的迪。请参阅此帖子。
我认为马克非常善于指出利弊,阅读他的帖子很有教育意义。我不同意他的所有结论,但TBH我很想和他一起做一段时间(不仅仅是几周,更长(,然后看看我是否同意。
话虽如此,我想为您提供一种处理抽象工厂模式用例的替代方法。由于我已经在SO上发布了它,因此除了将其链接:)之外别无他
法最后一点,我不知道有任何 IoC 容器具有像 Ninject 的 .ToFactory()
这样的Func<>
或接口工厂,它们早期实例化了依赖项。仅在执行Func<>
或调用 IFooFactory.CreateFoo()
方法后执行此操作。因此,自动生成的工厂尚未解决快速故障的问题。但是,所做的是,某些容器具有验证方法,这些方法验证类型是否可以实例化并且没有捕获依赖项。例如简单的喷油器