一、开闭原则解决的问题
首先不对开闭原则下定义,先看一个不遵循开闭原则的例子。
你自己组装电脑主机,首先购买各种配件,然后按照主机的组成结构拼装在一起,就可以愉快打游戏了。
image.png有一天,发现显卡带不动一款大型游戏了,所以你想换一块更好的显卡。but,当初买主板和显卡时,它俩的交互接口不是通用的,新买的显卡适配不了主板。
这时,你把主板换了,发现新主板又不适配硬盘了,最后把除了机箱电源之外的组件全部换新的,才升级完显卡。
为了升级显卡(扩展功能),把大部分组件都修改了(对修改不关闭);如果最初组装机器时,各组件的接口都是通用的,那么选择适配接口的显卡即可完成升级。
现在对开闭原则下个定义,开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。
二、什么是开闭原则
对扩展开放、对修改关闭,这又是开放、又是关闭的,听起来有些矛盾、难以理解,一个东西不可能即开放,又关闭吧?
是的,同一个东西不可能既开放、又关闭! 但是如果不是同一个东西呢?考虑上面装机的故事,多个组件组合在一起,构成了一台完整的主机;但是各组件又是单独的个体,可以对他们进行修改、扩展。既然动词扩展和修改的对象不是同一个,开放、关闭同时存在就成为了可能。
面向对象的世界中也是如此,多个对象通过一定的耦合,形成一个整体对外提供服务。那么什么是对修改关闭,对扩展开放呢?
对修改关闭:参考电脑主机的组成,各个组件之间的耦合,是有一套通用、统一接口,这套依赖关系是不会随着组件的不同而改变的,它是固定的,做到了“对修改关闭”。面向对象中,对修改关闭,意味着对象之间的依赖关系是固定的、不会随着需求变更而修改的。对象之间的依赖架构,是不可修改的,这是对修改关闭。
对扩展开放:世间唯一不变的就是改变,世界要向更好的方向发展。我们的需求是会不断变化的,要想保持进步,必须有可以改变的部分。在组装主机中,各个组件都可以独立升级,主机性能更加完美。面向对象中,对扩展开放,意味着可以在原有的对象组织关系中,轻松增加新特性。
三、实践开闭原则
要做到对修改关闭、构建一个稳固的对象依赖架构,需要高度抽象出对象的本质,依赖本质的抽象而不是具体的对象。因为本质是不会轻易改变的,通过本质依赖构建的结构也不会轻易发生改变。这对软件开发人员的抽象能力提出了很高的要求。
总结来说,就是抽象;从实践上来说,就是依赖接口、或者抽象类,而不要依赖具体对象。这也是另一条面向对象设计原则所倡导的:面向接口编程。
要做到对扩展开放,轻易增加新特性,需要把同类型的可变性封装在一起。试想如果内存的部分功能,和显卡都做到了同一个组件上,需要升级内存时,是不是也要把显卡也升级了?这对扩展开放了,但是开放得不够友好。
总结来说,就是封装可变性;从实践上来说,就是把同类特性封装到一个对象中,而不要散落在各处。这也是另一条面向对象设计原则所倡导的:单一职责。
四、开闭原则在其他领域的应用
不只是面向对象设计领域,其他领域也能找到开闭原则的影子。
redis cluster的数据分片设计:
-
寻找key对应的redis实例时,没有让hash(key)直接依赖redis实例,而是抽象出一层哈希槽,让hash(key)与哈希槽形成稳定的依赖关系(对修改关闭)
-
哈希槽与redis实例的关系时可变的,可以轻易进行水平扩展(对扩展开放)
网友评论