开闭原则(open/closed principle)到底是啥意思?

2021-11-16 10:33:46 +08:00
 x97bgt

SOLID 的提出者 Martin 是这么描述开闭原则 OCP 的

software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

但我不大理解。代码一旦写完,就不能修改,只能往上添东西了?这不是堆 shi 山吗?

5755 次点击
所在节点    程序员
60 条回复
admol
2021-11-16 14:45:13 +08:00
开闭原则讲的是代码的扩展性问题,添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。
Buges
2021-11-16 14:45:55 +08:00
这个很好理解,你用一个第三方库,但和你的需求有所出入,你包装一套接口使用就叫 extension ,你直接去改库代码就叫 modification 。你说哪个更容易维护?
Yuan2One
2021-11-16 15:38:24 +08:00
其实就是增加内聚,降低耦合
对修改关闭:不要变更各部件的中间耦合部分,不然会导致所有依赖部件都要修改
对拓展开放:提供耦合实现,包别的部件可以轻易接入实现拓展
开闭原则的典型实现就是接口
我是这么理解的
gaocc
2021-11-16 15:50:21 +08:00
开放-封闭原则:对于持续迭代的项目,对于代码实体(类,模块,函数等)应该可以拓展,但不能修改;
开闭原则的好处是,在实际开发中,需求会多次改变但程序还是会相对稳定,从而使第一个版本后可以不断推出新版本。
2i2Re2PLMaDnghL
2021-11-16 16:04:37 +08:00
一个好的设计思想不应该局限于面向对象。

『这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。』

即是指提供『改变行为』的『插槽』
sort 的例子很明显,它通常提供一个 cmp 或者 key 函数的插槽。(当然,它通常提供了默认项,方法不一。)
更一般的例子是允许虚函数的抽象类,这个「虚函数表」就是那个插槽的客观实体。在编写抽象类时,你根本无从得知 obj.meth 到底是哪段内存中的代码。
georgetso
2021-11-16 16:27:42 +08:00
我感觉生命周期函数就是典型的复合开闭原则的设计.
生命周期函数本身就是提供给开发者扩展的 anchors, 同时不提供对象生命周期耦合关系的修改.
Innovatino
2021-11-16 16:51:49 +08:00
@libook
你的字多,你解释得也清楚。对于初学者来说够用了
darknoll
2021-11-16 16:55:27 +08:00
就是非侵入式,好处非常多
11232as
2021-11-16 17:06:07 +08:00
我个人理解就是尽量写粒度小的接口,然后在其基础上扩展、搭积木,避免写出那种包含一堆业务功能的方法。有点类似 Unix 编程思想里那个小既是美的感觉...?
dranfree
2021-11-16 17:09:19 +08:00
提供合理的扩展接口,在不修改核心代码的情况下,为系统增加功能。
FrankFang128
2021-11-16 17:11:23 +08:00
你的理解跟我的一样,我也觉得应该拥抱修改。除了对外 API
cryboy007
2021-11-16 17:57:30 +08:00
个人理解就是后续迭代不能影响现有功能
powerman
2021-11-16 18:05:15 +08:00
理解软件工程以及代码架构,关键是你要理解一点,

那就是不管 SOLID 原则以及设计模式的运用都是在做同一件事情,将易变的部分与不易变的部分隔离开来,更抽象的说法就是提供一套机制将策略与机制分离开来。

一般来说机制是难以变化的,而策略经常变化,一个软件,框架可以看成一种机制,因为写业务大多只是使用框架提供的功能,而并非去对框架进行修改,也就是说框架是不易变的,而业务逻辑是易变的,在这里框架是一种机制,业务代码是一种策略,例如 mybaits 框架,你编写 SQL xml 这就是一种策略,而 mybatis 框架本身就是一套机制。

做到这一点首先要对目标域的问题进行分解,发现难以变化的部分以及容易变化的部分,
例如 Spring 框架,通过 XML 加载 bean 定义的 或者 通过类扫描 加载 Bean 定义 又或者你要自定义一种其它的方式来加载 bean 定义,总儿言之加载 Bean 定义的方式是多种变化的,但是对 bean 本身来说,bean 需要解决的领域问题是确定的,是不会变化的,所以抽象出来一个 BeanDefination 用于初始化 Bean ,这样 BeanDefination 作为一种初始化 bean 数据结构的机制是不易变的,而具体加载生成 BeanDefination 的策略是易变的。

类似的设计思想还有很多,例如 BeanFactoryPostProcessor BeanPostProcessor 也是这样一种思想,框架本身提供了一种回调的机制,至于你如何客制化 BeanDefination 或者 客制化 Bean 的 instance 则是一种具体的策略。

像 sort 方法 排序算法本身是一种机制,至于如何比对两个对象的大小则是一种具体的策略。

总而言之,在代码层面上,你要时刻去思考,你的代码如果要复用,那么被复用的代码应该提供一种什么样的机制,让具体的策略去使用去组合起来方便,而不至于需要修改你原本的,至于具体的设计模式 反倒并没有那么重要,
fkdog
2021-11-16 18:20:20 +08:00
楼上这些解释都是复读机。

你想想 spring 就明白了,我们在做 spring 相关插件扩展功能的时候,是不是都不需要修改 spring 核心类库的任何代码?
因为 spring 提供了足够多的扩展点能让我们非常方便的实现需求。

所以我们平时写代码时也应该像 spring 一样,通过类似模板等设计模式为变化较多的部分提供充分的可扩展空间。

细化到具体的某个业务么,就是类似支付系统。一开始接的支付宝支付,然后加了微信支付,if-else 可以满足需求。但是如果后边需要对接银联等支付,if-else 就会堆成屎山。在不修改原来业务主流程的情况下,通过扩展多个支付方法子类显然是更优雅的选择。

不过国内的互联网,如果不是搞基础框架、业务中台一类的,搞这些开闭原则啥的其实没多大意义,相反还会徒增烦恼。视项目规模和维护价值自己做取舍。
jiayong2793
2021-11-16 18:39:46 +08:00
保持类的最小颗粒度
OnlyO
2021-11-16 18:50:39 +08:00
@fkdog #54 你这个回复解释的比较到位.
xylophone21
2021-11-16 19:11:42 +08:00
一看你的客户是谁,二看你的业务这么开展.

@fkdog 的例子就很好, 你做支付的,经常性的接一个新的支付(夸张了)就是你的业务, 对接的这个人就是你的客户. 让他们每次干活的时候不需要修改你的代码.

但你的代码里, 假设原来用的是 mysql,你要切到 postgresql, 当然可以改你的代码.

再深入一步, 你当然也可在设计之初,就想好, 如果后面要改数据库, 我是不是可以不修改, 留好扩展. 但这不是你的业务, 想多了就是过度设计了. 除非你做这个支付系统的目的, 就是为了测试各种不同的数据库系统. 或者说以及有现成的轮子了, 用起来几乎没有代价 (比如这个换数据库的例子,从某种意义上来说,就是有轮子的情况)

再进一步, 换数据库虽然不是你的业务, 但他其实是框架的业务, 比如对 Spring Data JPA 来说, 他的一个目标就是不论他的用户用什么数据库, 核心流程都差不多. 那么换数据库就是他的业务.
lux182
2021-11-17 09:23:26 +08:00
@powerman 非常好,学习了
summerLast
2021-11-17 09:49:12 +08:00
对扩展开放 对修改关闭 核心是怕修改引入新的问题 ;
如果 没有引入新的问题而且行为表现一致的化 是可以修改的 ,假如代码已经腐烂了,是在腐烂外面一层层的包裹 最后腐烂的代码成为了软件珍珠 还是重构这是权衡之后做出的选择。
很多时候你会发现有很多相对立的概念好像都对, 核心是缺失了上下文导致的,而这些对立的概念其实就是一种策略,策略也是需要有上下文来决定使用那种更好一些,不要去迷信各种模式,他们是抽象的概念 既然抽象必有信息的丢失,找到自己写代码的感觉
powerman
2021-11-17 09:51:20 +08:00
@fkdog 复读机? 也不算吧,光是理解问题域,发现问题域中 难以变化的部分 将其剥离出来形成一种机制 与易变化的部分隔离 就要对领域知识有充足的了解,而并非对代码技术有多么深层次的理解。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/815704

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX