OCP – 可扩展的设计
在本节中,我们将看到 OCP 如何帮助我们编写可以添加新功能而无需更改代码本身的代码。这起初听起来像是不可能的,但它自然地从 DIP 与 LSP 的结合中产生。
OCP 导致代码对扩展开放但对修改关闭。我们在研究 DIP 时看到了这个想法的应用。让我们从 OCP 的角度回顾我们所做的代码重构。
让我们从 Shapes
类的原始代码开始,如下所示:
public class Shapes {
private final List<Shape> allShapes = new ArrayList<>();
public void add(Shape s) {
allShapes.add(s);
}
public void draw(Graphics g) {
for (Shape s : allShapes) {
switch (s.getType()) {
case "textbox":
var t = (TextBox) s;
g.drawText(t.getText());
break;
case "rectangle":
var r = (Rectangle) s;
for (int row = 0; row < r.getHeight(); row++) {
g.drawLine(0, r.getWidth());
}
}
}
}
}
添加一种新的形状需要修改 draw()
方法内部的代码。我们将添加一个新的 case
语句来支持我们的新形状。
修改现有代码有几个缺点,如下所述:
-
我们使之前的测试无效。这现在是与我们测试过的代码不同的代码。
-
我们可能会引入一个错误,破坏现有对形状的支持。
-
代码将变得更长且更难以阅读。
-
我们可能会有几个开发人员同时添加形状,并在合并他们的工作时产生合并冲突。
通过应用 DIP 并重构代码,我们最终得到了这个:
public class Shapes {
private final List<Shape> all = new ArrayList<>();
private final Graphics graphics;
public Shapes(Graphics graphics) {
this.graphics = graphics;
}
public void add(Shape s) {
all.add(s);
}
public void draw() {
all.forEach(shape -> shape.draw(graphics));
}
}
我们现在可以看到,添加一种新的形状不需要修改这段代码。这是 OCP 在工作的一个例子。Shapes
类对定义新类型的形状开放,但在添加新形状时对修改关闭。这也意味着与 Shapes
类相关的任何测试将保持不变,因为这个类的行为没有差异。这是一个强大的优势。
OCP 依赖于 DI 来工作。它或多或少是应用 DIP 的结果的重新表述。它还为我们提供了一种支持可交换行为的技术。我们可以使用 DIP 和 OCP 来创建插件系统。
添加新类型的形状
为了看看这在实践中是如何工作的,让我们创建一个新的形状类型,RightArrow
类,如下所示:
public class RightArrow implements Shape {
public void draw(Graphics g) {
g.drawText(" \" ");
g.drawText("-----");
g.drawText(" /");
}
}
RightArrow
类实现了 Shape
接口并定义了一个 draw()
方法。为了证明使用这个类不需要改变 Shapes
类中的任何内容,让我们回顾一些使用 Shapes
和我们的新类 RightArrow
的代码,如下所示:
package shapes;
public class ShapesExample {
public static void main(String[] args) {
new ShapesExample().run();
}
private void run() {
Graphics console = new ConsoleGraphics();
var shapes = new Shapes(console);
shapes.add(new TextBox("Hello!"));
shapes.add(new Rectangle(32, 1));
shapes.add(new RightArrow());
shapes.draw();
}
}
我们看到 Shapes
类正在以完全正常的方式使用,没有改变。事实上,使用我们的新 RightArrow
类唯一需要做的改变是创建一个对象实例并将其传递给 shapes
的 add()
方法。
OCP
使代码对新行为开放,但对修改关闭。 |
OCP 的力量现在应该很清楚了。我们可以扩展代码的能力并限制更改。我们大大减少了破坏已经工作的代码的风险,因为我们不再需要更改该代码。OCP 是管理复杂性的一种很好的方式。在下一节中,我们将看看剩下的 SOLID 原则:ISP。