【问题标题】:Is this a right or viable implementation of the Abstract Factory pattern?这是抽象工厂模式的正确或可行的实现吗?
【发布时间】:2014-11-10 16:40:38
【问题描述】:

灵感来自Mark Seemann's post: Pattern Recognition: Abstract Factory or Service Locator?

我希望像这样写一个抽象工厂:

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&lt;T&gt;,因为每种类型:

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&lt;T&gt;() 方法,而Abstract Factory 使用的是非通用的@987654338 @方法。

感谢您指出这一点,@PrestonGuillot! =)

也许我在这里过于复杂了......所以这是我的模型:

电子邮件指示符

public abstract EmailIndicator { 
    int EmailCount { get; set; }
    DateTime OldestDateTimeReceived { get; set; }
}

EmailIndicatorA

public class EmailIndicatorA : EmailIndicator { }

EmailIndicatorB

public class EmailIndicatorB : EmailIndicator { }

EmailIndicatorC

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

就我的理解而言,作为解决方案的第一步,我会尝试以下方法:

IRepositoryFactory

public interface IRepositoryFactory<T> where T : class, new() {
    T Create();
}

IEmailIndicatorARepositoryFactory

public interface IEmailIndicatorARepositoryFactory 
    : IRepositoryFactory<EmailIndicatorARepository> {
    EmailIndicatorARepository CreateEmailIndicatorARepository();
}

IEmailIndicatorBRepositoryFactory

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... }
}

这是更好的方法吗?

我有点迷茫和困惑。

【问题讨论】:

  • 如果您需要EmailIndicatorAEmailIndicatorBEmailIndicatorC 的实例来创建CustomerServiceIndicatorsModel 的实例,为什么不在构造函数中包含这些实例?看起来你没有做任何需要你的 sn-p 抽象工厂的事情。
  • 你是对的。也许我的例子没有很好地选择,我想不出其他任何东西。另外,Abstract Factory 模式的实现呢?
  • 不管怎样,您链接的博客文章直接回答了您的问题仙女“抽象工厂是一个泛型类型具有非泛型 Create 方法;服务定位器是一个 具有泛型 Create 方法的非泛型类型。”,你属于后者。
  • 再想一想,你是对的。一路走来,我一定很迷茫。我很难理解Abstract Factory 模式的要点及其好处。
  • @PrestonGuillot:请查看我的问题编辑。 =)

标签: c# ninject ninject-extensions abstract-factory


【解决方案1】:

我发现Wikipedia article 的图表很有启发性。

我认为“抽象工厂”这个名称在 .net / c# 世界中有点误导。你只需要abstract class,以防语言不支持接口或者你想共享实现(但请记住favor composition over inheritance)。

既然你提到了 Mark Seeman,我想提炼一下我对他关于 IoC 和工厂的一些帖子的解释:

  • 不要使用服务定位器
  • 创建一个组合根。理想情况下,它应该在一开始就一次性实例化所有对象。
    • 因此,您应该将您的对象设计为无状态的并且只处理数据 - 只要有可能(数据和处理逻辑分离)。这意味着您只需实例化或多或少的数据类/数据结构,它们没有任何依赖关系。所有具有依赖关系的对象都是在应用程序启动时一次性创建的。
  • 后期创建不会快速失败
    • 如果您使用后期创建,无论您将其称为服务定位器还是工厂,这都会降低可测试性,因为问题在应用程序启动时不可见,而只有在您稍后使用/创建对象时才会可见。李>
    • 为了解决这个问题,创建要由工厂创建的对象的所有依赖项已经@composition root。仅后期创建实际对象。
    • 这是使用抽象工厂模式完成的。

所以根据你的例子,这将是:

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 容器了。相反,他通常更喜欢穷人的di。见this post

我认为 Mark 非常擅长指出优缺点,阅读他的帖子很有教育意义。我不同意他的所有结论,但是 TBH 我很想和他一起在一个项目上工作一段时间(不仅仅是几周,更长)然后然后看看我是否同意。

话虽如此,我想为您提供另一种方法来处理抽象工厂模式的用例。因为我已经在 SO 上发布了它,所以除了link it :)

最后一点,我不知道有任何 IoC 容器具有 Func&lt;&gt; 或像 Ninject 的 .ToFactory() 这样的接口工厂,它们早期实例化了依赖项。只有在您执行Func&lt;&gt; 或调用IFooFactory.CreateFoo() 方法后才会执行此操作。 因此,自动生成的工厂尚未解决快速失败的问题。 然而,已经完成的是一些容器具有验证方法来验证类型可以被实例化并且没有Captive Dependencies。例如Simple Injector

【讨论】:

  • 受您的方法启发,我最终决定继续使用提供程序,我发现这是一种更简单的方法,尽管它实际上具有相同的目的,即基于一些实例化实例一种上下文或依赖关系。顺便说一句,我总是很感激你的回答。它们总是启发了我更正确地使用 DI 和 IoC 的道路。谢谢。
猜你喜欢
  • 2012-05-08
  • 1970-01-01
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-12
相关资源
最近更新 更多