9

工厂方法模式(不要与工厂或抽象工厂模式混淆)是否违反了开放/封闭原则

更新:为了澄清,我指的是具体类上有静态工厂方法的场景。例如(这来自 FMP 上的 Wikipedia 页面):

class Complex 
{
    public static Complex fromCartesian(double real, double imag) {
        return new Complex(real, imag);
    }

    public static Complex fromPolar(double modulus, double angle) {
        return new Complex(modulus * cos(angle), modulus * sin(angle));
    }

    private Complex(double a, double b) {
       //...
    }
}

私有构造函数不会阻止类被子类化,即扩展吗?

难道不需要修改类来支持新的工厂方法吗?例如,如果该类最初只有 fromCartesian 而后来需要 fromPolar,那么是否必须修改该类以支持这一点?

这两个都不违反开/关吗?

4

3 回答 3

5

不,它根本不违反开放/封闭原则。

打开/关闭意味着您可以修改系统的工作方式,而无需修改已经存在的代码。您可以扩展代码并以不同的方式使用它,但旧代码仍然完好无损,不需要重新测试。

工厂方法模式将根据指定的参数创建不同类型的对象。如果操作正确,工厂方法实际上适用于开/关原则。但是,如果您创建一个新类,然后希望工厂方法创建该类型的新对象,则必须更改工厂方法。

虽然,如果您有某种配置文件或工厂方法读取的那种类型的文件,那么您不必更改工厂方法......只需配置文件,然后指示将由以下方式创建的对象工厂方法。

于 2010-03-04T17:11:35.993 回答
4

工厂模式本质上并不是OCP的违反者。

这取决于你如何采取Complex进一步的行为。

如果Complex需要支持生成新类型的Complex对象,而您选择Complex通过添加新fromX方法来支持它们进行修改,那么这意味着Complex成为违反OCP的,因为Complex必须重新打开进行修改:

class Complex 
{
    public static Complex fromCartesian(double real, double imag) {
        return new Complex(real, imag);
    }

    public static Complex fromPolar(double modulus, double angle) {
        return new Complex(modulus * cos(angle), modulus * sin(angle));
    }

    //class opened for modification
    public static Complex fromOtherMeans(String x , String y) {
        return new Complex(x, y);
    }
}

您可以将此问题推送到某种文本文件或属性文件中,以免除您必须更改 java 类的责任,但这并不妨碍您必须在解决方案的这个区域按顺序编写额外的逻辑支持新类型的Complex.

根据Complex您的设计中的使用情况(各种类型有何不同?您如何使用它们?),有一些替代选项可能适用。

一种这样的OCP友好替代方法是子类Complex化以提供额外的工厂方法。子类是如何Complex扩展但不修改的最简单说明。

在这种情况下,另一个对OCP友好的替代方法是装饰器模式。不断地装饰,能够创建尊重 OCP 的新变体,因为它没有被修改,而是通过用新功能包装它来扩展。ComplexComplexComplexComplex

第三种选择可能是改变结构,Complex以便其计算由组合提供。这将为您提供使用策略模式区分不同行为的机会Complex

关于工厂模式的事情是它有助于上下文代码尊重OCP有人可能会使用上述技术之一,以便与他们的 Factory 类保持在OCP的右侧,但您的同事可能会看一看对象图,质疑在单个工厂上拥有对象图是否明智,并将其简化回一个工厂,这将带您回到第一个示例。

在这种情况下,与其试图改变工厂模式的实现以尊重SOLID原则,不如考虑一下为什么要使用它

于 2014-06-24T09:03:43.177 回答
2

没有。从您的维基百科链接:

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭

覆盖工厂方法是扩展。您正在创建一个新课程。您不会更改现有课程。您必须(希望通过您的 IoC 容器的配置)替换原始的子类。

于 2010-03-04T17:10:34.000 回答