软件修养 -- 开闭原则(OCP :Open Closed Principle)

定义

开闭原则(OCP :Open Closed Principle):软件中的实体(类,模块,函数等等)应该对扩展开放,对修改封闭,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为

优点

开闭原则对扩展开放,对修改封闭,该特性在产品化的环境中是特别有价值的,在这种环境中,改变源代码需要代码审查,单元测试以及诸如此类的用以确保产品使用品质的过程。遵循这种原则的代码在扩展时并不发生改变,因此无需上述的过程

思考

  • 开闭原则为了什么?

    开闭原则的主旨是为了拥抱变化,并且在变化过程中保持系统的可维护性代码的重用性

  • 何为对扩展开放?

    模块对扩展开放,就意味着需求变化时,可以对模块扩展,使其具有满足那些改变的新行为。换句话说,模块通过扩展的方式去应对需求的变化

  • 何为对修改关闭?

    模块对修改关闭,表示当需求变化时关闭对模块源代码的修改,当然这里的“关闭”应该是尽可能不修改的意思,也就是说,应该尽量在不修改源代码的基础上面扩展组件。

  • 如何做到开闭原则?

    开闭原则只是一个思想,没有具体实际操作方法。其他原则都是为了实现这个开闭思想的一些方法和工具

案例

案例的主题与依赖倒置原则篇一致。

小华在设计的时候,使用依赖倒置的思想,前瞻性地设计了 Logger 接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Logger {
  log(text: String): void;
}

class PrintLogger implements Logger {
  log(text: String) {
    console.log("print");
  }
}

class Client {
  logger?: Logger;

  start() {
    logger?.log("开始");
  }
}

let client = new Client();
let logger = new PrintLogger();
client.logger = logger;

client.start();

因此,后面小明在应对需求变更的时候,直接扩展一个具体的实现即可,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
interface Logger {
  log(text: String): void;
}

class WebLogger implements Logger {
  log(text: String) {
    console.log("writeToWeb");
  }
}

class PrintLogger implements Logger {
  log(text: String) {
    console.log("print");
  }
}

class Client {
  logger?: Logger;

  start() {
    logger?.log("开始");
  }
}

let client = new Client();
let logger = new WebLogger();
client.logger = logger;

client.start();

注意点

  • 前瞻性设计

    想要遵守开闭原则,就需要一个设计合理的系统。可以说在做系统设计(概要设计、详细设计)的时候就要考虑到未来的扩展和改变

  • 用抽象构建框架,用实现扩展细节

    因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节,用从抽象派生的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。当然前提是抽象要合理,要对需求的变更有前瞻性和预见性才行。

  • 开闭原则是总纲

    单一职责原则表达实现类要职责单一;里氏替换原则表达不要破坏继承体系;依赖倒置原则表达面向接口编程;接口隔离原则表达在设计接口的时候要精简单一;迪米特法则表达要降低耦合。而开闭原则是总纲,表达要对扩展开放,对修改关闭。

延伸阅读


CatchZeng
Written by CatchZeng Follow
AI (Machine Learning) and DevOps enthusiast.