何时使用抽象工厂模式?

18

我试图简要描述何时使用工厂,为了让我和我的团队都能明白。我看到了以下相关问题,有所帮助:

基于这些链接和其他一些来源(列在底部),我总结出以下内容:

何时使用抽象工厂模式:

  • 当您使用接口变量或“new”运算符
    • 例如:User user = new ConcreteUserImpl();
  • 并且您编写的代码应该在某个时刻可测试/可扩展。

解释:

  • 接口本质上意味着多个实现(有利于单元测试)
  • 接口变量意味着OCP和LSP兼容的代码(支持子类化)
  • 'new'操作符的使用会破坏OCP/DI,因为高度耦合的类很难测试或更改
  • "我是否需要为每种对象类型创建一个工厂?那似乎有些过分了。"

    • 不需要,您可以拥有一个(或几个)工厂来生产许多(通常是相关的)对象类型
    • 例如:appFactory.createUser(); appFactory.createCatalog();等等。

    不适用工厂的情况:

    • 新对象非常简单且不太可能被子类化
      • 例如:List list = new ArrayList();
    • 新对象对测试不感兴趣
      • 没有依赖项
      • 没有执行任何相关或长时间运行的工作
      • 例如:Logger log = new SimpleLogger();

    参考资料:

  • 利是科夫代碼置換原則

  • 我的问题是:我的总结准确吗?有没有我忽略的内容?

    提前致谢。


    我讨厌重新激活旧的线程,但是...我不同意像上面那样新建记录器。在不同的测试环境中,我经常运行不同的日志记录器。我会配置环境以使用特定的记录器,并将其注入到需要记录的对象中。(我甚至可能会注入记录器数组。) - aridlehoover
    3个回答

    5
    我建议在你想要使用特定实现时不要使用工厂。以List为例,我知道我需要一个ArrayList因为我需要随机访问,我不想依赖于工厂获取这个结果而是亲自实现。 相反的,在我不想知道具体子类的情况下,我可以使用工厂并让它担心要实际实例化哪个对象。
    我认为你应该在“何时使用抽象工厂模式”中添加一条记录,即“您实际上并不关心要获取哪个具体子类”,并在“何时不要使用工厂”中添加相反的内容。
    编辑:注意避免使用通用工具建立工厂工厂工厂

    好的 - 假设我想要一个缓存线程池的特定优势: ExecutorService pool = Executors.newCachedThreadPool();然而,在我的单元测试中(出于任何原因),我对线程池中完成的工作结果不感兴趣,我也不想每次运行我的测试时等待工作完成。那么我难道还是需要使用一个工厂来为测试提供桩/模拟线程池吗?在我看来,当你考虑测试时,你希望将大部分的代码视为不关心实际实现,否则你就会写出不可测试的代码。 - Luke
    是的,没错。通常大部分代码都不关心实际的实现方式。在选择 ArrayListLinkedList 之间时,你需要关注它们的性能特征,这是为数不多的需要关注的情况之一。你说得对:在测试时,尽可能忽略实际的实现方式是最好的。 - Cameron Skinner

    3

    一般情况下,当你希望通过外部配置来切换实现时,可以使用它。

    JDBC和JAXP是很好的例子。如果需要更多示例,请查看这个答案


    那不意味着要使用一个IoC容器吗? - dexter
    请问您能否详细说明在何时使用自定义工厂而不是IoC来通过配置切换对象类型? - dexter
    @Max:这取决于API是否要在IoC容器上运行。 - BalusC

    1

    抽象工厂模式提供了一种封装具有某些共性的具体工厂的方式,这意味着它们实现相同的接口/抽象类。

    每当您想要控制对象的初始化而不是将控制权交给消费者时,您需要使用工厂模式。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接