1. 概念
- 开闭原则是编程中最基础、最重要的设计原则
- 一个软件实体如类、模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。也就是说我可以对实现去扩展功能,但是我调用方的代码不应该进行修改,还是原来的调用方式。也就是用抽象构建框架,用实现扩展细节
- 当软件需要变化时,尽量通过**扩展(增加功能)软件实体的行为来实现变化,而不是通过修改(改变功能)**已有的代码来实现变化
- 编程在中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则
2. 案例对比说明
现在有一个画图形的类,其中有一个public void drawShape(Shape s)方法,这里Shape是一个抽象类,其中有一个type成员变量。然后分别有三角形、矩形、圆形分别继承了Shape类并且有自己的type值。drawSahpe方法通过判断type变量的值,然后分别调用drawRectangle、drawCircle、drawTriangle方法来完成相应图形的绘制
代码实现:
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Triangle());
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
}
}
// 当我增加一个三角形的类
// 这是一个用于绘图的类[使用方]
class GraphicEditor {
// 接收一个Shape对象,然后根据Type来绘制不同的图形
public void drawShape(Shape s) {
if (s.m_type == 1)
drawRectangle(s);
else if (s.m_type == 2)
drawCircle(s);
else if (s.m_type == 3) {
drawTriangle(s);
}
}
public void drawRectangle(Shape r) {
System.out.println("矩形");
}
public void drawCircle(Shape r) {
System.out.println("圆形");
}
// 绘制三角形
public void drawTriangle(Shape shape) {
System.out.println("三角形");
}
}
class Shape {
int m_type;
}
class Rectangle extends Shape {
Rectangle() {
super.m_type = 1;
}
}
class Circle extends Shape {
Circle() {
super.m_type = 2;
}
}
class Triangle extends Shape {
Triangle() {
super.m_type = 3;
}
}
问题阐述:
现在当我增加一个新的图形,比如平行四边形,那么我首先需要创建一个平行四边形的类,同样的继承Shape类,然后设置type值为4,接着需要在drawShape方法中增加一个条件判断,然后再新增一个相应的绘制平行四边形的方法,才能够实现这个需求。
而这里,drawShape类作为使用方,根据开闭原则, 调用方的代码不应该被修改,所以这样的功能扩展明显是违背了开闭原则的。
那么应该怎么修改原来的设计方式才能够符合开闭原则呢?
考虑到每一个图形都有一个draw方法,不如将draw作为一个抽象方法在Shape抽象类中定义,这样每一种图形继承Shape类的时候实现自己的独有的draw方法即可。这样连type变量都可以省去,我们直接将Shape换成一个接口更加方便。
修改后的代码实现:
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawRectangle(new Rectangle());
graphicEditor.drawRectangle(new Triangle());
graphicEditor.drawRectangle(new Circle());
}
}
// 当我增加一个三角形的类
// 这是一个用于绘图的类[使用方]
class GraphicEditor {
// 接收一个Shape对象,然后直接调用Shape的draw方法
public void drawShape(Shape s) {
s.draw();
}
public void drawRectangle(Shape r) {
System.out.println("矩形");
}
public void drawCircle(Shape r) {
System.out.println("圆形");
}
// 绘制三角形
public void drawTriangle(Shape shape) {
System.out.println("三角形");
}
}
abstract class Shape {
int m_type;
public abstract void draw();
}
class Rectangle extends Shape {
Rectangle() {
super.m_type = 1;
}
@Override
public void draw() {
System.out.println("长方形");
}
}
class Circle extends Shape {
Circle() {
super.m_type = 2;
}
@Override
public void draw() {
System.out.println("圆形");
}
}
class Triangle extends Shape {
Triangle() {
super.m_type = 3;
}
@Override
public void draw() {
System.out.println("三角形");
}
}
当然这里我没有把Shape换成一个接口。。。
分析:
当我们把设计方式换成这样之后,注意到我们的drawShape方法直接使用的是Shape对象的draw方法来实现目的,省去了复杂繁琐的if-else判断。然后,当我们需要增加一个新的图形。只需要继承Shape类并且实现draw方法,就能够实现相应的功能。也就是说,功能扩展新增了代码,但是调用方的代码不用修改。完美地符合开闭原则,对扩展开放,对修改关闭!
3. 总结
根据开闭原则,我们在设计之初应该考虑到,当我新增功能的时候,怎么维持调用方代码不变。也就是调用方仍然使用那套代码,而有新的需求的调用方也可以使用新的功能,这就要求我们在新增功能的时候是通过扩展而不是通过修改来实现。 总之就是,让使用者有更好的体验!