开闭原则定义
Robert C. Martin 认为这个原则是面向对象设计中最重要的一个原则,但他不是第一个定义这个原则的人,Bertrand Meyer 在1988年写的一本书《Object-Oriented Software Construction》,其中解释了开闭原则:软件实体(类,模块,方法等)应该对扩展开放,对修改关闭。
这个原则的主要思想是很棒的,它告诉我们写代码时,当添加新功能时不应该修改现有的代码,这样阻止了我们修改一处代码所依赖它的代码都要修改。但是有点可惜的是,Bertrand Mayer 当时建议用 继承 去实现这个原则,具体描述如下:
“一个类是被关闭的,因为它已经被编译放在一个类库里,后面被其他代码引入调用,但是它也是开放的,因为一个新类可以继承它,添加新特性,添加子类,不需要修改原有的父类和调用父类的代码”。
这个思想流行了一些年,直到有人对这个原则提出了更好的解释,例如,Robert C. Martin 在他的文章里和 Joshua Bloch 在他的书 《Effective Java》都提到,通过继承的方式,引入了子类和父类的紧耦合,子类需要依赖父类的实现细节。
因此,Robert C. Martin ,还有其他人对开闭原则重新做了定义,利用多态实现开闭原则,使用接口替代继承的方式,通过类实现接口的方式来扩展新功能,接口对修改关闭。
这种实现方式的主要好处是接口引入了一个新的抽象实现松耦合,接口实现类是相互独立的,之间不存在共享代码。如果你认为两个实现类之间存在共享代码是有好处的,你可以通过继承或者组合的方式实现。
样例
编写一个计算器程序,支持加减乘除操作,首先我们定义一个顶级接口 CalculatorOperation
:
public interface CalculatorOperation {}
定义一个加法
类,实现两个数字的求和计算
public class Addition implements CalculatorOperation {
private double left;
private double right;
private double result = 0.0;
public Addition(double left, double right) {
this.left = left;
this.right = right;
}
// getters and setters
}
类似减法
代码,
public class Subtraction implements CalculatorOperation {
private double left;
private double right;
private double result = 0.0;
public Subtraction(double left, double right) {
this.left = left;
this.right = right;
}
// getters and setters
}
定义一个代表计算器的类,调用加法和减法
public class Calculator {
public void calculate(CalculatorOperation operation) {
if (operation == null) {
throw new InvalidParameterException("Can not perform operation");
}
if (operation instanceof Addition) {
Addition addition = (Addition) operation;
addition.setResult(addition.getLeft() + addition.getRight());
} else if (operation instanceof Subtraction) {
Subtraction subtraction = (Subtraction) operation;
subtraction.setResult(subtraction.getLeft() - subtraction.getRight());
}
}
}
上面的例子,看上去没什么问题,但是不符合开闭原则,当有新的功能需添加时,我们需要修改类 Calculator
。
现在我们把上面的例子稍作修改,让它符合开闭原则,首先在接口 CalculatorOperation 中添加方法 perform
public interface CalculatorOperation {
void perform();
}
加法的实现类如下:
public class Addition implements CalculatorOperation {
private double left;
private double right;
private double result;
// constructor, getters and setters
@Override
public void perform() {
result = left + right;
}
}
我们再加一个除法的实现类
public class Division implements CalculatorOperation {
private double left;
private double right;
private double result;
// constructor, getters and setters
@Override
public void perform() {
if (right != 0) {
result = left / right;
}
}
}
类Calculator
基于接口 CalculaotrOperatotion
实现,当有新的实现时,无须更改此处的代码
public class Calculator {
public void calculate(CalculatorOperation operation) {
if (operation == null) {
throw new InvalidParameterException("Cannot perform operation");
}
operation.perform();
}
}
SOLID 系列文章: