定义:在面向对象编程领域中,开闭原则规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。该特性在产品化的环境中是特别有价值的,在这种环境中,改变源代码需要代码审查,单元测试以及诸如此类的用以确保产品使用质量的过程。遵循这种原则的代码在扩展时并不发生改变,因此无需上述的过程。
原理:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
问题由来:假设我们的产品已经有一个版本V1
上线,在实体A
中有功能a
需要修改,若我们直接修改源代码可能会给旧代码中引入问题代码,这将可能迫使我们重新对实体A
进行重构,且需要对原有代码进行重新测试。
产生原因:软件开发人员在扩展、更新、迭代的过程中修改源代码或者系统抽象不合理均可能产生不符合开闭原则的代码。
解决办法:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变。
应用场景:我们有如下需求:V1版本中,产品提出要求实现可以实现牛和老虎的软件实体,且都可以描述他们的行为,如吃这个行为,那我们可能写出如下代码:
public class Main {
public static void main(String[] args) {
Animal cow = new Animal("牛");
Animal tiger = new Animal("老虎");
cow.eat();
tiger.eat();
}
}
class Animal {
private final String species;
public Animal(String species) {
this.species = species;
}
public void eat () {
if ("牛".equals(species)) {
System.out.println("牛:吃草");
} else if ("老虎".equals(species)) {
System.out.println("老虎:吃肉");
}
}
}
虽然我们已经实现了需求,但是可能在某一次版本迭代过程中产品要求再加上羊和狮子,那么我们就需要再原有代码中添加if...else
语句来实现功能,这明显违背了开闭原则,因此我们的代码应该改为如下形式:
public class Main {
public static void main(String[] args) {
Animal cow = new Cow();
Animal tiger = new Tiger();
((Behavior)cow).eat();
((Behavior)tiger).eat();
}
}
interface Behavior {
void eat();
}
abstract class Animal {
protected String species;
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
}
abstract class Herbivores extends Animal implements Behavior {
@Override
public final void eat() {
System.out.println(species + ":吃草");
}
}
abstract class Predator extends Animal implements Behavior {
@Override
public final void eat() {
System.out.println(species + ":吃肉");
}
}
class Cow extends Herbivores {
public Cow() {
this.species = "牛";
}
}
class Tiger extends Predator {
public Tiger() {
this.species = "虎";
}
}
可以看到每一种动物都有一个对应的软件实体,因此我们只要在迭代时添加Sheep
和Lion
两个类即可实现扩展需求。
小提示:只要代码时遵循其余五个设计模式原则代码就是遵循开闭原则的,开闭原则更像是其余五个原则的考核指标,若代码符合开闭原则表示代码抽象合理。