一、定义
- 类、模块和函数应该对拓展开放,对修改关闭。
- 当软件需要变化时,尽量通过拓展软件实体的行为而不是通过修改已有的代码来实现变化。
二、应用举例
我们来用一个画图的功能来说明。
类图:
说明: Shape类
是所有图形类的基类; GraphicEditor类
接收 Shape
对象并根据其参数 type
判断画出什么图形。
2.1 方式一 : 不遵循开闭原则
我们在实现这个画图功能后,还要新增一个三角形的画法。
// 图形基类
class Shape {
int type;
}
// 继承Shape的矩形类
class Rectangle extends Shape {
Rectangle() {super.type = 1;}
}
// 继承Shape的圆形类
class Circle extends Shape {
Circle() {super.type = 2;}
}
// 新增三角形类,继承Shape类
class Triangle extends Shape {
Triangle() {super.type = 3;}
}
// 绘图类
class GraphicEditor {
public void drawShape(Shape shape) {
if (shape.type == 1) drawRectangle();
if (shape.type == 2) drawCircle();
// 新增三角形绘制
if (shape.type == 3) drawTriangle();
}
private void drawRectangle() {System.out.println("绘制矩形");}
private void drawCircle() {System.out.println("绘制圆形");}
// 新增绘制三角形方法
private void drawTriangle() {System.out.println("绘制三角形");}
}
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
// 新增绘制三角形
graphicEditor.drawShape(new Triangle());
}
}
此种实现方式的优缺点
- 优点:易于理解,操作简便。
- 缺点:违背了
OCP
原则,我们新增绘制三角形的功能时,我们需要新增一个三角形类并修改绘图类GraphicEditor
中的逻辑代码同时新增一个方法。
2.2 方式二 : 遵循开闭原则
我们可以对方式一中的内容进行改进:把 Shape
类修改为抽象类,提供一个抽象的 draw()
方法,并让其子类进行实现。
这样当我们需要新增图形时,只需让新的图形类继承 Shape
类并实现 draw()
方法即可,而无需修改绘图类。
// 图形基类,抽象类
abstract class Shape {
public abstract void draw();
}
// 矩形类
class Rectangle extends Shape {
@Override
public void draw() {System.out.println("绘制矩形");}
}
// 圆形类
class Circle extends Shape {
@Override
public void draw() {System.out.println("绘制圆形");}
}
// 三角形类
class Triangle extends Shape {
@Override
public void draw() {System.out.println("绘制三角形");}
}
// 绘图类
class GraphicEditor {
public void drawShape(Shape shape) {
shape.draw();
}
}
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
graphicEditor.drawShape(new Triangle());
}
}
三、注意事项和细节
开闭原则想表达的另一层意思 : 用抽象构建框架,用实现扩展细节。因为抽象灵活性非常好,适应性广,使用得当就可以保持软件架构的稳定;而软件中易变的细节,可以用从抽象类派生的实现类进行扩展,当需求发生变化时,只需重新派生一个实现类进行拓展就行了。