SOLID之OCP

开闭原则 OCP Open-Closed Principle

设计良好的计算机软件应该易于扩展,同时抗拒修改

换句话说,一个良好的计算机系统应该在不需要修改的前提下就可以轻易被扩展

遵循开闭原则设计出的模块具有两个特征:

  1. “对于扩展是开放的”,当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为
  2. “对于更改是封装的”,对模块进行扩展时,不必改动原有的代码

其实这也是研究软件架构的根本目的。如果对原始需求的小小延伸就需要对原有的软件系统进行大幅修改,那么这个系统的架构设计显示是失败的

一个好的软件架构设计师会努力将旧代码的修改需求量降至最小,甚至为0

这原则看着很矛盾,需要扩展,但却又不要修改;那么如何实现这个原则呢?

抽象,面向接口编程

模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,所以它对于更改可以是关闭的。同时,通过从这个抽象体派生,也可以扩展此模块的行为

client,server都是具体类,client使用server

如果client想使用另一个server对象,那么需要修改client中使用server的地方

显然这样违反了OCP

在新的设计中,添加了ClientInterface接口,此接口是一个拥有抽象成员函数的抽象类。Client类使用这个抽象类。如果我们希望client对象使用不同的server,只需要从clientinterface类派生一个新类,client无需任何修改

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
30
31
32
interface ClientInterface
{
public void Message();
//Other functions
}

class Server:ClientInterface
{
public void Message();
}

class Client
{
ClientInterface ci;
public void GetMessage()
{
ci.Message();
}
public void Client(ClientInterface paramCi)
{
ci=paramCi;
}
}

//那么在主函数(或主控端)则
public static void Main()
{
ClientInterface ci = new Server();
//在上面如果有新的Server类只要替换Server()就行了.
Client client = new Client(ci);
client.GetMessage();
}

OCP设计类与模块时的重要原则,但是在架构层面,这项原则意义更重大。

在设计时,可以先将满足不同需求的代码分组(SRP),然后再来调整这些分组之间的依赖关系(DIP)

IOC是不是也有OCP的味道

OCP算是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术的巨大好处(灵活性,可重用性以及可维护性)

然而,并不是说只要使用一种面向对象语言就得遵循这个原则。对于应用程序中每个部分都肆意地进行抽象同样不是一个好主意。正确的做法是,开发人员应该仅仅对程序中呈现出频繁变化的那些部分进行抽象,拒绝不成熟的抽象和抽象本身一样重要

在一个复杂的软件中为什么会建议“尽量”不要违背OCP?

最核心的原因就是一个现有逻辑的变更可能会影响一些原有的代码,导致一些无法预见的影响。这个风险只能通过完整的单元测试覆盖来保障,但在实际开发中很难保障单测的覆盖率。OCP的原则能尽可能的规避这种风险,当新的行为只能通过新的字段/方法来实现时,老代码的行为自然不会变


Common Closure Principle(CCP)共同封闭原则

CCP延伸了开闭原则(OCP)的“关闭”概念,当因为某个原因需要修改时,把需要修改的范围限制在一个最小范围内的包里

一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中所有的类。一个更简短的说法是:一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。CCP原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该属于同一个包。

CCP还是解决分布式单体可怕的反模式的法宝

在现流行的微服务架构中,按业务能力和子域以及SRP和CCP进行分解是将应用程序分解为服务的好方法

公众号:码农戏码
欢迎关注微信公众号『码农戏码』