热衷学习,热衷生活!😄

沉淀、分享、成长,让自己和他人都能有所收获!😄

一、开闭原则定义

开闭原则是面向对象的可复用设计的第一块基石,是最重要的面向对象设计原则。定义如下:

开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。

在开闭原则的定义中,这个软件实体可以是一个软件模块、一个由多个类组成的局部结构或者一个独立的类。

二、开闭原则描述

任何系统都需要面临一个很重要的问题,就是它们的需求会随着时间的推移而发生变化。当软件系统需要面对新的需求时,我们应该尽量保证系统的设计架构是稳定的。如果一个软件系统满足开闭原则,那么它可以非常方便的进行扩展,而且可以在不修改先有代码的前提下进行扩展,使得软件系统在拥有适应性和灵活性的同时具有很好的稳定性和扩展性。随着软件规模越来越大,软件寿命越来越大,软件维护成本越来越高,设计满足开闭原则的软件系统也越来越重要。

为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。在实际开发中,可以为系统定一个相对稳定的抽象层,而将不同的实现行为移至具体的实现层中实现。Java提供了很好的抽象、接口类机制,我们可以通过它们定义系统的抽象层,再通过具体类进行扩展。如果需要修改系统的行为,无须对抽象层进行修改,只要新增新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上进行系统功能的扩展,达到开闭原则。

三、开闭原则栗子

不满足开闭原则

先有一个需求需要显示各种类型的图表,比如饼状图和柱状图等,为了支持多种类型的图表显示方式,原始设计如下:

1
2
3
4
5
public class PieChart {
public void display(){

}
}
1
2
3
4
5
public class BarChart {
public void display(){

}
}
1
2
3
4
5
6
7
8
9
10
11
public class ChartDisPlay {
public void display (String type) {
if (type.equals("pie")) {
PieChart pie = new PieChart();
pie.display();
} else if (type.equals("bar")) {
BarChart bar = new BarChart();
bar.display();
}
}
}
  • 在这个设计中,如果需要新增一个新的图表类,如折线图LineChart,则需要修改ChartDisPlay类的display()方法的源代码,增加新的判断逻辑,不满足开闭原则,而且随着时间推移如果类型越来越多,维护成本大大提高。

重构,满足开闭原则

在本栗子中,由于在ChartDisPlay类的display方法中针对每一种图表类型编程,因此新增新的图表类不得不修改源代码。我们可以通过抽象的方法进行重构,使之新增新的图表类时无需修改源代码,满足开闭原则。设计如下:

1
2
3
public abstract AbstractChart {
protected abstract void display();
}
1
2
3
4
5
6
7
public class PieChart extends AbstractChart {

@Override
public void display(){

}
}
1
2
3
4
5
6
7
public class BarChart extends AbstractChart {

@Override
public void display(){

}
}
1
2
3
4
5
6
7
8
9
10
public class ChartDisPlay {
private AbstractChart chart;

public void setChart(AbstractChart chart){
this.chart = chart;
}
public void display (String type) {
chart.display();
}
}
  • 新增AbstractChart抽象类,定义display()显示方法
  • PieChartPieChart继承AbstractChart重写display()显示方法。
  • ChartDisPlay类型进行修改,新增属性AbstractChart chart并定义一个setChart()方法来设置实际的图表对象,然后通过该对象调用display()显示方法。

重构完之后,这个时候如果我们需要新增一个折线图表类型LineChart只需新增LineChart类并继承AbstractChart类型重写display()方法,向ChartDisplay中注入一个LineChart对象即可,续修修改先有的代码,满足开闭原则,扩展性很高,大大的降低了维护成本。