我们什么时候应该使用工厂方法模式? (而不是作文)

时间:2017-03-02 00:45:11

标签: java oop design-patterns polymorphism factory-method

根据GOF书,Factory method pattern

  
    

定义用于创建对象的接口,但让子类决定实例化哪个类。 Factory方法允许类将实例化延迟到子类。

  

模式的结构

public abstract class Factory {
    public abstract IProduct createProduct();
    private void performCriticalJob(){
        IProduct product = createProduct();
        product.serve();
    }
    public void executeJob(){
        //some code
        performCriticalJob();
        //some more code
    }
}


public interface IProduct {
    public void serve();
}
  1. 工厂需要一个对象(其具体类未知或其具体类可能根据不同的应用程序类型而改变)来执行任务。

  2. 由于它不知道要实例化哪个类,因此为所需的对象类型设置了一个标准契约,该契约放在一个接口中。

  3. 基础工厂类声明abstract方法,以返回上面定义的类型对象interface。 它允许子类决定并提供对象创建的实现。

  4. 为完成任务,它需要一个只需调用abstract方法即可获取的对象。

  5. 问题 赞成继承继承。 上面的工厂方法使用继承来获得具体的产品。子类也需要实现createProduct,它将创建并返回ConcreteProduct。如果从中删除抽象方法(这使得Factory类成为非抽象类),而不是Subclassing Factory类。现在,Factory类可以由新类组成,具体的产品对象可以在其中注入,如下例所示。

    为了实现Factory方法模式定义的场景中的意图,为什么正常的多态性没有被如下使用?我知道工厂方法模式有更多我想要的东西,但是依靠继承的优势组合,我找到了下面的方法,在同一场景中解决同样的问题,比Factory method更好的方式。 Factory method优于以下方法的优势是什么?

    public abstract class Factory {
        private void performCriticalJob(IProduct product){
            product.serve();
            //some code
        }
        public void executeJob(IProduct product){
            //some code
            performCriticalJob(product);
            //some more code
        }
    }
    

    现在,用户不必通过实现createProduct方法要求用户创建子工厂类并返回产品的具体对象,而是直接向executeJob提供实现IP产品的具体类的对象。

    [编辑]感谢您的回答和评论,但我在评论和答案中表达的同样思想也带来了一些困惑。我研究了 GOF 工厂方法模式。它使用 Framework的示例来创建各种类型的文档的应用程序。我的问题是对这项研究后引起的怀疑。

    这些网站和博客只是基于对模式的理解的反映,他/她可能或可能没有读过,理解模式的实际意图。理解课程不是主要目标。应该研究设计模式,考虑什么样的情况以及出现哪些问题是哪种最佳解决方案遵循良好的OOP原则(或者最少地违反这些原则并将它们排除在外并且有充分的理由这样做)。最佳解决方案是任何design pattern解释的解决方案。 GOF是一本标准书,可以很好地解释它。但仍然存在一些差距或疑问,这是这个问题的主要原因。

4 个答案:

答案 0 :(得分:1)

  

我知道工厂方法模式有更多我缺少的东西,但是依靠继承的优势组合,我找到了以下方法,在同一场景中解决同样的问题

使用 Factory Method 模式而不是普通旧组合时,您可以获得以下几个优点:

<强> 1。关注点分离和开放原则:您为每个相关的对象组创建一个工厂子类。此工厂子类仅负责创建属于特定组的产品。 ABCProductFactory仅关注创建ABCProduct1ABCProduct2等。CDEProductFactory仅关注创建CDEProduct1CDEProduct2等等。对于每个新产品组,您都要创建一个新的子类,而不是修改现有的类。如果您使用合成方法,其他一些类将负责创建产品并将其传递到您的Factory。随着您的产品种类增加到ZZZProduct1ZZZProduct2等等,这个类很快会爆炸到一个巨大的大小,有太多的if-else条件来检查要创建的产品子类。您最终会意识到这一点并定义一个类来创建每个相关的产品组。

<强> 2。产品创建和产品处理有一个合同:在这种情况下,工厂方法模式与模板方法模式非常相似,因为它为需要执行的操作指定了模板创建后的对象。这使您可以确保在创建产品后,它将始终执行与工厂创建的任何其他产品相同的步骤。将此与您问题中的 Composition 示例进行比较,其中IProduct创建后应执行的步骤没有固定的合同。我可以创建一个名为Factory2的类,其中包含一个名为public的{​​{1}}方法。没有什么能迫使我在performCriticalJob中使用executeJob方法。即使我向Factory2添加executeJob方法,也没有任何东西强迫我在Factory2内调用performCriticalJob。您可以使用模板模式解决此问题。

现在应该很清楚, Factory Method 模式基本上将对象创建和对象处理绑定在一个类中。使用您的作文示例,您将拥有大量移动部件,而无法管理它们应如何协同工作。

底线:在您的情况下,当您希望对象创建和对象处理具有固定合同时,使用工厂方法模式,以便所有对象都通过相同的处理一旦创建。使用您的组合示例,其中在创建对象后,不需要合同来执行步骤。

答案 1 :(得分:1)

由于我们知道工厂方法模式依赖于继承,因此如果不从工厂方法开始就简单地问,我们何时应优先考虑继承而不是组成?< / p>

我们已经知道该问题的答案是“ 不常见”,因为GoF告诉我们总体上倾向于构图。但是,在现实世界中,存在继承。动物界可能是典型的例子。当继承实际上发生在域中时,在我们的代码中以相同的方式对其建模是有意义的。那时候我们也应该考虑工厂方法模式。仅当您已经确定继承适合于上下文时才考虑使用它。首先选择继承,然后选择创建模式。不要引入Factory Method,然后再强制建立继承关系,否则该继承关系将不适用。

一个更自以为是的答案是:永远不要在现代代码中使用工厂方法模式。坚持组成关系,所有关系都在依赖注入容器中连接在一起。 GoF模式不是建议。它们是1980年代设计的集合。与Singleton由于其负面副作用而现在被认为是反模式而非模式相比,Factory方法在现代代码中的适用性极低。在少数情况下可能合适,您会知道它的原因,因为您已经在使用继承。

答案 2 :(得分:0)

再次阅读您提供的定义:

  

创建对象定义一个接口,但让子类决定实例化哪个类。 Factory方法允许类将实例化延迟到子类。

有点与你的第一句话相矛盾:

  

工厂需要一个对象(其具体类未知或其具体类可能根据不同的应用程序类型而改变)执行任务

工厂目的是创建对象,而不是执行任务

事实上,如果事件执行任务只是为了能够为你创建对象。 从工厂拿到物品后,就可以完成关键工作了。

关于赞成组合而不是继承,工厂方法也可以为你构建对象并传回一个对象组合。

工厂模式有很多很好的例子 - 例如Wikipediablackwasp

编辑 - 关于GoF Application示例

Application的GoF示例使用Template Method Factory MethodApplication定义工厂方法及其周围的一些操作,但Application的子类决定创建哪个Document

您已建议,不要使用工厂方法。相反,要在其他地方创建Document并注入&#34;到Application(a.k.a依赖注入)。 您还没有描述Document将在何处创建(它仍然可以是工厂) 现在,Application子类对Document的创建没有任何控制权。这完全改变了系统的行为和设计。

它并不意味着它的坏或好,它只是一种不同的方法。
在现实场景中,您必须仔细检查手头的问题,并确定哪种设计最适合。

答案 3 :(得分:0)

你的问题是,你坚持认为你的Factory类只有一种客户端总是使用该类,通过扩展它(代码#1)或将新创建的IP产品传递给它的方法(代码#2)。这类客户的目的是使Factory能够接收新创建的IP产品。

不关心上述所有事情的普通客户怎么样?这些甚至不关心班级是否是工厂。因此,他们不需要像代码#2那样需要IP产品的方法。

确实,您应该将Factory课程重命名为其他课程(例如XXX),该课程不是“工厂”!但它的一些方法是“工厂”。你看,模式名称是“工厂方法”,而不是“工厂”或“工厂对象”。相比之下,在抽象工厂模式中,抽象工厂实际上是工厂对象。

P / S:正确的组合方法是将抽象工厂传递给XXX的构造函数。