这是抽象工厂模式的正确或可行的实现吗?

本文关键字:实现 抽象 工厂 模式 | 更新日期: 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();
}

具有三个实现,一个用于EmailIndicatorAEmailIndicatorBEmailIndicatorC。现在为了使论证有意义,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() 方法后执行此操作。因此,自动生成的工厂尚未解决快速故障的问题。但是,所做的是,某些容器具有验证方法,这些方法验证类型是否可以实例化并且没有捕获依赖项。例如简单的喷油器