【问题标题】:Factory design pattern and violation of OCP (Open-Closed Principle)工厂设计模式与OCP(开闭原则)的违反
【发布时间】:2020-07-24 20:15:59
【问题描述】:

this tutorial的工厂明显违反了OCP。每次在系统中添加一个形状时,我们都需要在工厂中添加它来支持它。 我正在考虑另一种实现,我想知道是否有任何缺点。

public class ShapeFactory {

   //use getShape method to get object of type shape
   public Shape getShape(Class<? extends Shape> shapeType){
      return shapeType.newInstance();
   }
}

这个实现看起来不违反 OCP,也不复杂。有什么原因我找不到任何提到它的工厂设计模式教程?

【问题讨论】:

  • @Andreas,我可以在.newInstance之前使用.getConstructor方法并将参数传递给newInstance
  • @LeffeBrune 它没有提到我要问的实现
  • 它没有提到它@Youssef13,因为你的实现不实用。正如 Andreas 提到的,要能够构造任何重要的对象,您需要传递构造函数参数。如果你的工厂不知道它创建的类型,它就无法知道如何实例化它们。
  • 此外,工厂通常对客户端隐藏具体类型,但在您的情况下,您的客户端必须使用具体类型调用工厂。有点矛盾。

标签: java factory open-closed-principle


【解决方案1】:

这种方法有一些缺点。

首先,当传递给 getShape 的类需要构造函数参数时,.newInstance 将失败。例如:

public class Circle {
   public Circle(int diameter) {
      //something
   }
}

您可以通过使用getConstructor 并找出要传递的参数来进行反思,但这很复杂且容易出错。而且您在编译时失去了类型安全性。工厂类如何知道将什么值传递给直径?

工厂设计模式的优点之一是调用者在调用时不必知道使用了什么实现类。举个例子:

public class ShapeFactory {
   public Shape getCircle(int diameter){
      return new Circle(int diameter);
   }
}

无论何时调用此方法,调用者都不需要依赖 Circle 类:

Shape s = ShapeFactory().getCircle(10);
s.draw();

这样,只有ShapeFactory 依赖于Circle。因此,当您更改或替换 Circle 类时,只需更改 ShapeFactory

要创建符合 OCP 的 shape 程序,我们可以将 ShapeFactory 替换为依赖注入框架。下面的代码是伪代码,展示了它是如何工作的

// define the classes
class Circle {}
class Square {}

// for every class, provide a factory method. These do not have to exist inside a factory class.
public Circle createCircle() {
    return new Circle(10)
}

public Circle createSquare() {
    return new Square(42, 5)
}


public class Painter {
    //when a class requires an instance of the Circle class, the dependency injection framework will find `createCircle`, call it and add the result as an argument here.
    public Painter(Circle circle) {
       circle.draw();
    }
}


//when you need a painter object, we don't create it yourself but let the dependency framework do the heavy lifting
Painter p = dependencyframework.getInstanceOf(Painter.class)

有许多 Java 依赖注入框架,但它们都是这样工作的。

这些框架执行您建议的完全相同的事情(例如 newInstancegetConstructor,但还有很多),它们只是隐藏了所有反射的复杂性。

【讨论】:

  • 这很好地解释了它。您能否在答案中添加一个不违反 OCP 的良好实现?
  • 在这种情况下,自己实施一个完美的符合 OCP 标准的工厂会受到更多的伤害。您将不得不进行大量反思,失去类型安全并引入很多复杂性。那可能不值得。看看依赖注入 (softwareengineering.stackexchange.com/questions/220640/…),这可能会让你更接近答案。
【解决方案2】:

我认为@hfontanez 对"Does the Factory Pattern violate the Open/Closed Principle?" 问题的回答涵盖了这一点。如果您要添加新的 Shape 子类,您还必须以某种方式添加一种方法来创建它们的实例。假设您无法修改原始 ShapeFactory,因为它是第三方库的一部分,但您可以继承或装饰原始工厂以添加对新形状的支持。扩展示例如下所示:

public class AdvancedShapeFactory {
  private final ShapeFactory factory = new ShapeFactory();

  public Shape getShape(String shapeType) {
    if (shapeType.equalsIgnoreCase("PENTAGON")) {
      return new Pentagon();
    } else {
      return factory.getShape(shapeType);
    }
  }    
}

如果最初虚构的“形状”库的设计者想要通过形状类型轻松创建新形状,他们可以实现一个注册表:

public class ShapeRegistry {
  private static final Map<String, Class<Shape>> shapeTypes = new HashMap<>();

  public void registerShape(String shapeType, Class<Shape> shapeClass) {
    shapeTypes.put(shapeType, shapeClass);
  }

  public Shape getShape(String shapeType) throws InstantiationException, IllegalAccessException {
    if (shapeTypes.containsKey(shapeType)) {
      return shapeTypes.get(shapeType).newInstance();
    }
    return null;
  }
}

值得一读 dependency injectionGuice 作为一个很好的例子。

【讨论】:

  • 应该在哪里调用 registerShape 方法?
  • 我希望库在初始化时会注册自己的形状。添加新形状类型的库也将负责注册它们。
  • 我尝试在静态初始化程序中调用它,但静态初始化程序从未执行。因此,没有注册任何内容。
  • 我尝试使用反射在工厂本身的静态初始化程序中进行注册。客户端(调用者)不会依赖子类或知道它们,并且它不违反 OCP。这样做有什么缺点吗?
  • 这没什么错,大多数依赖注入框架实际上都依赖于反射,尤其是与注解结合使用时。
猜你喜欢
  • 1970-01-01
  • 2021-12-20
  • 2021-08-24
  • 2011-01-23
  • 1970-01-01
  • 1970-01-01
  • 2013-07-26
  • 1970-01-01
  • 2018-06-11
相关资源
最近更新 更多