开闭原则

231 阅读4分钟

1. 概念

  1. 开闭原则是编程中最基础、最重要的设计原则
  2. 一个软件实体如类、模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。也就是说我可以对实现去扩展功能,但是我调用方的代码不应该进行修改,还是原来的调用方式。也就是用抽象构建框架,用实现扩展细节
  3. 当软件需要变化时,尽量通过**扩展(增加功能)软件实体的行为来实现变化,而不是通过修改(改变功能)**已有的代码来实现变化
  4. 编程在中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则

2. 案例对比说明

现在有一个画图形的类,其中有一个public void drawShape(Shape s)方法,这里Shape是一个抽象类,其中有一个type成员变量。然后分别有三角形、矩形、圆形分别继承了Shape类并且有自己的type值。drawSahpe方法通过判断type变量的值,然后分别调用drawRectangledrawCircledrawTriangle方法来完成相应图形的绘制 代码实现:

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. 总结

根据开闭原则,我们在设计之初应该考虑到,当我新增功能的时候,怎么维持调用方代码不变。也就是调用方仍然使用那套代码,而有新的需求的调用方也可以使用新的功能,这就要求我们在新增功能的时候是通过扩展而不是通过修改来实现。 总之就是,让使用者有更好的体验!