首页 > 技术文章 > 软件设计原则之开闭原则(Open-Closed Principle)

xiaotutu365 2019-01-07 11:31 原文

定义

类应该对扩展开放对修改关闭。用抽象构建框架,用实现扩展细节。

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。

主要特征

  1. 对于扩展是开放的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。
  2. 对于修改是关闭的,这意味着对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。

典型场景

在Java设计模式中,装饰者模式是一个很好的例子,完全遵循开放-关闭原则。

代码讲解

这里先以一个简单的例子讲解开放-关闭原则

第一版需求:首先抽象出一个课程接口,然后Java,Python,UI等的课程来实现这个接口,打印出相关的课程信息。

定义一个接口Course

public interface Course {    
    /** 
     * 课程Id 
     * @return 
     */
    Integer getId();    
    /** 
     * 课程名称
     * @return 
     */
    String getName();    
    /** 
     * 课程价格
     * @return 
     */
    Double getPrice();
}

定义一个接口Course的实现类JavaCourse:

public class JavaCourse implements Course {
    private Integer id;    
    private String name;    
    private Double price;    
    
    public JavaCourse(Integer id, String name, Double price) {
        this.id = id;        
        this.name = name;        
        this.price = price;    
    }    
    
    @Override
    public Integer getId() {        
        return id;    
    }    
    
    @Override    
    public String getName() {        
        return name;    
    }
    
    @Override
    public Double getPrice() {
        return price;    
    }    
    
    @Override    
    public String toString() {        
        return "JavaCourse{" + "id=" + id + ", name='" + name + '\'' + ", price=" + price + '}';    
    }
}

定义测试类:

public class Test {
    public static void main(String[] args) {
        Course javaCourse = new JavaCourse(1, "Java课程", 100d);
        System.out.println(javaCourse);
    }
}

打印出如下信息:

JavaCourse{id=1, name='Java课程', price=100.0}

上面对应的类图如下:

第二版需求:这个时候又来了一个需求,Java课程由于做活动,打8折,这个时候应该如何实现呢?

有如下几种实现方式:
1.在接口Course中,增加打折的方法getDiscountPrice(),如下:

    Double getDisCountPrice();

这样JavaCourse实现这个方法即可。

2.直接在JavaCourse类中的getPrice方法中,直接将price*0.8。

这两种方式虽然实现起来很快,但是在应对需求变化时,是明显不足的。比如:

  • 要求不同价格的折扣粒度不同
  • 要求显示原来的价格

等等。

这个时候我们可以定义个折扣类来继承自JavaCourse,来实现对价格打折的支持,如下:

public class JavaDiscountCourse extends JavaCourse {
    public JavaDiscountCourse(Integer id, String name, Double price) {
        super(id, name, price);
    }

    @Override
    public Double getPrice() {
        return super.getPrice() * 0.8;
    }
}

然后修改测试类,如下:

Course javaCourse = new JavaDiscountCourse(1, "Java课程", 100d);
System.out.println("课程Id: " + javaCourse.getId() + ", 课程名称: " + javaCourse.getName() + ", 课程价格: " + javaCourse.getPrice());

输出如下:

课程Id: 1, 课程名称: Java课程, 课程价格: 80.0

这个时候有个问题,无法获取到原价,我们可以在JavaDiscountCourse增加获取原价的方法,如下:

public Double getOriginPrice() {
        return super.getPrice();
}

修改测试类,如下:

Course course = new JavaDiscountCourse(1, "Java课程", 100d);
JavaDiscountCourse javaCourse = (JavaDiscountCourse) course;
System.out.println("课程Id: " + javaCourse.getId() + ", 课程名称: " + javaCourse.getName() + "课程原价:" + javaCourse.getOriginPrice() + ", 课程价格: " + javaCourse.getPrice());

这时我们的类图如下:

到此,我们的程序就修改完成了,而且不会对之前的代码有任何影响,这也就是开始提到的开放-关闭原则。

推荐阅读