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

时间:2014-11-10 16:40:39

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

灵感来自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<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。 1}}使用非通用的Create()方法。

感谢您指出,@ PrestonGuillot! =)

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

EmailIndicator

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

这是一种更好的方法吗?

我在这里迷茫和困惑。

1 个答案:

答案 0 :(得分:1)

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

我认为这个名字&#34;抽象工厂&#34;在.net / c#世界中有点误导。如果语言不支持接口或您想要共享实现(但请记住favor composition over inheritance),则只需要abstract class

既然你提到马克·西曼,我想提炼一下他对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()绑定和接口来解决,这些绑定和接口不是通用的,而是特定的(基本上与上面的抽象工厂基本相同!)。

我认为你应该仔细权衡专业人士的角色。

马克还说他很少再使用IoC容器了。相反,他最常喜欢穷人的爱情。请参阅this post

我认为马克非常擅长指出职业选手,并且阅读他的帖子非常有教育意义。我不同意他的所有结论,但TBH我很乐意和他一起工作一段时间(不仅仅是几周,更长),然后看我是否同意或不

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

之外别无选择。

作为最后一点,我不知道任何具有Func<>的IoC容器或Ninject&#39; .ToFactory()之类的接口工厂,这些工厂可以提前实例化依赖关系。只有在您执行Func<>或调用IFooFactory.CreateFoo()方法后才会执行此操作。 因此,自动生成的工厂尚未解决快速失败的问题。 但是,已经完成的是某些容器具有验证方法,这些验证方法可以验证类型是否可以实例化,并且没有Captive Dependencies。例如Simple Injector