写点什么

你懂 SOLID 原则吗?

  • 2020-12-25
  • 本文字数:4341 字

    阅读完需:约 14 分钟

你懂SOLID原则吗?

虽然 SOLID 原则不能时刻有效指导编码落地,理解这些原则背后的设计理念,让你迈出了第一步,接下来,你需要做的是在前进的路上,不断地进行编码实践、思考总结,将其内化。


做了这么多年的面向对象编程还是写出违背 SOLID 原则的代码,一看都懂、一做就被怼,敏感度不够,如何是好?


  • 难道 SOLID 原则本身就有错?

  • 难道我不应该涉水 OOD?

  • ……


请先屏住呼吸,我们来看看 SOLID 叫什么:


  • Single Responsibility Principle,单一职责原则

  • Open Close Principle,开闭原则

  • Liskov Substitution Principle,里氏替换原则

  • Interface Segregation Principle,接口隔离原则

  • Dependency Inversion Principle,依赖倒置原则


原则通常较为抽象,别说刚接触 OO 的程序员,有一定经验的人也不一定吃得透。要想提高对 OO 原则的敏感度,第一步,我们要清楚 SOLID 到底在讲什么?本文,袁 Sir 的 SOLID 创业故事将为你揭开一层面纱。


设计小而精美的工具箱,提高顾客体验


袁 Sir 创业初期经营了一家五金器具共享租用店,遇到两类顾客,A 顾客需要剪刀、锤子、扳手、电锯,B 顾客需要锤子、扳手、水果刀、梅花刀。袁 Sir 为了图省事,把这个六把工具同时装在一个工具箱里,每次都把装有 6 把工具的工具箱给 A 或 B 租用。


经过几次借还,袁 Sir 收到顾客的抱怨,为什么呢?因为袁 Sir 这么做增加了 A 和 B 的负担,首先多了两把工具,扛来抗去的累得多。其次,他们拿回去之后会发现:”咦,这两个是啥,我没要这个呀?莫非是增值服务?但我要用来做啥呢?” 结果 A 从未用过水果刀和梅花刀,但 A 还要保管好这两工具,增加了保管的成本。同理,B 顾客也会面临同样的问题。为了提高顾客的体验,袁 Sir 把这两类顾客的工具单独用两个更小的工具箱装好,分别提供给 A 和 B,并得到了两类顾客的一致好评。


袁 Sir 从一开始图一时的方便,塞给顾客他本不需要的东西,经过反思后,从顾客需求出发,只提供顾客想要的东西。投其所好,是他创业悟出的第一个处世之道。


在软件设计中,ISP 提倡不要将一个大而全的接口扔给使用者,而是将每个使用者关注的接口进行隔离。



让客户坚信徒弟能代替师傅干活


袁 Sir 勤勤恳恳经营的五金租用店 2 年,挣了一大把辛苦钱,正好赶上房地产兴起的年头,家装市场跟着兴起,他果断买下一家知名的家具厂,并跟一家私人工匠所展开了合作。


SJ 是工匠所的一名木匠学徒,从师 3 年有余,书架、衣柜、餐桌,门、窗这些家具已经能够到跟他的师傅如出一辙了。


袁 Sir 每次合作都是直接跟 SJ 的师傅对接,而在实际执行过程中,师傅有时候因体力跟不上,就交给 SJ 去做,好在每次生产出来的家具都能够让袁 Sir 满意。几次合作下来,袁 Sir 便相信:徒弟继承了师傅的手艺,师傅能做的这些家具,徒弟也一定能做得一模一样。


2017 年,房地产彻底疯了,袁 Sir 一次性采购订单多出了以往 2 倍。为了如期交付,SJ 的师弟小 Y 也参与进来。不幸的是,个性十足的小 Y 打造衣柜和门窗的方式跟师傅的风格差别很大,设计出来的家具让袁 Sir 一脸懵逼,始料不及的三丈大火烧尽后,袁 Sir 果断地中断了跟这家工匠会所的合作…


[LSP 解读] 小 Y 将其师傅的手艺继承后,但自己又擅自修改了设计风格,结果打造的家具跟袁 Sir 的期望不一致,这就好比子类替换了父类,产生了不一致的行为,软件不能正常运行,这种继承设计存在潜在的问题。


在软件设计中,LSP 重点强调:对使用者来说,能够使用父类的地方,一定可以使用其子类,并且预期结果是一致的。


简而言之,子类不应该去修改父类已有的功能(在 Java 中,体现为子类重写了父类中非抽象的方法)。如果你的继承体系中出现了这种替换后不一致的现象,需要警惕你的继承是否合理了。


关于代码示例,请在文末参考阅读文章《让里氏替换原则为你效力》。



清晰良好的合作协议,让彼此更加信任


经过上一次不愉快的合作,袁 Sir 虽然少挣了一笔钱,还生了一肚子气。但台风来,猪想飞你也挡不住。在房地产彻底疯了的这几年里,袁 Sir 又捞了好几大桶金。有了钱能做什么呢?当然把公司做大,再挣更多的钱呀。


袁 Sir 秉着 ‘麻雀虽小,五脏不能不全’ 的原则,成立了财务部、法务部、行政部、人事部、市场部、销售部,由于期初业务量没有大到招架不住,各部门都是光杆司令,而且人事、行政、财务、法务四个部门由小蔡独管(袁 Sir 流露出了资本家的面貌)。


袁 Sir 要核对一个报销单,就直接找财务部的小蔡办了,要举办一场年会还得找小蔡,想了解上个月的销售业绩,他去找了销售部的老肖,当然找的最多的还是市场部老史。


经过大半年的快速发展,各部门从光杆司令变成了群狼作战。袁 Sir 仍然按照之前的方式办事,但遇到了烦恼 – 前天报销单还是小蔡在处理,昨天变成了小吴,今天又换成了小任。当然,其他部门也产生了相同的现象。苦思冥想,袁 Sir 出了一招:部门的负责人制定一个固定的服务列表,比如财务部小蔡提供服务列表:


  1. 核对报销单

  2. 处理报销

  3. 发工资

  4. 招聘新同事

  5. 评审绩效

  6. 举办年会

  7. 审核营业执照


袁 Sir 此时要处理报销、发工资,只用找小蔡沟通就好,至于具体谁去执行,袁 Sir 压根不需要知道。当然其他部门跟财务部合作也轻松了很多。此后袁 Sir 对所有部门都进行了整改。


此时袁 Sir 终于才体会到当老板的一丁点美好了。下班合上电脑前,他敲下了一句话:部门之间得通过约定好的协议来合作,而不用关心对方内部由谁做协议所规定的事项。


[DIP 解读] 一开始,袁 Sir 直接找部门中某个具体执行的人去沟通工作,此时袁 Sir 依赖的是具体实现细节,每次执行的人发生变化会增加袁 Sir 的沟通成本。他让小蔡提供了一个服务清单,小蔡充当了一个抽象/接口,此后他只用找小蔡沟通即可,袁 Sir 从依赖具体实现细节转变到依赖抽象。


在软件设计中,DIP 提倡使用者依赖一个抽象的服务接口,而不是去依赖一个具体的服务执行者,从依赖具体实现转向到依赖抽象接口,倒置过来。



各司其职,工作效率会大大提升


袁 Sir 起初让小蔡统管了人事、行政、财务、法务四个部门,业务扩张后也只是给小蔡多招了 10 来个人。小蔡大脑里就不得不绷紧四根弦,他要给部门各个同事合理分配任务,确保公司能够正常开展日常活动、人员考核培养、工资发放等工作,当然还有一些诸如工商督查、账务审核、信息安全审核等敏感的活动。


暂且不说小蔡有多累,关键是有一次捅了个篓子,差点没葬送公司前程。


一天,工商局来了几个人审核公司的营业资质,就在小蔡跟工商局负责人交涉的过程中,其他管理账目的两位同事因为一项没法对齐的数据起了点小争执,全然忘记还有工商局调查人员在,口角中抖了一些账目相关的敏感信息,好在小蔡反应快,及时进行了制止,当然也做了一些额外的安抚工作,把这事给糊弄过去。


这事传到袁 Sir 耳中,加上小蔡多次跟袁 Sir 反馈业务太杂,不但累且容易出篓子,袁 Sir 这才把几个部门进行了拆分,人事部交给了小任,行政部交给了小邢,法务由老发掌管。


经过几个月的验证,让袁 Sir 欣喜的是,运营成本不但没有增加,员工的工作状态也比之前大有改善。


从此,财务部门小蔡提供的服务列表:


  1. 核对报销单

  2. 处理报销

  3. 发工资


人事部门小任提供的服务列表:


  1. 招聘新员工

  2. 员工绩效考核

  3. 员工培养


行政部小邢提供的服务列表是:


  1. 举办年会

  2. 接待来宾


法务部提供的服务列表是:


  1. 审核营业执照

  2. 审核信息安全


周五晚上 10 点,袁 Sir 走出办公室大门,哼着小曲自言自语道:还得专门的人干专门的事呀,勿贪多,贪多必失…


袁 Sir 睡前打开电脑,在备忘录写下:分离关注点(专注)是做好一件事的有效途径,如果同时需要兼顾多项不太相关的工作,注意力就必须在不同的上下文切换,会大大降低工作效率,更糟糕的是可能会出现一些意外的灾难。敲完欣慰睡去。


在软件设计中,SRP 提倡让一个类只处理一组相关的事情,控制了它的变化方向,后期也能更好的定位。如果引发变化的因素很多,会导致类的职责过多,难以维护,常见的上帝类就是这么形。


引入中间人读书大使,解放自己


经过几年的发展,袁 Sir 的公司俨然已经步入正轨,他开始思考公司文化建设方面的事情。


袁 Sir 一直对读书非常重视,他想在公司把读书运动搞起来,每周搞一次分享大会。起初他提议由行政部和人事部来牵头搞,两个部门轮流来,自己直接分别跟两个部门负责此事的负责人对接。


两个月过去了,公司的读书氛围得到大大提升,书架遍布公司各个角落,水果时间也从八卦转向了读书心得的分享。


欣慰之余,袁 Sir 也感觉有点力不从心。经常出现这种现象:有时候去他找人事部的小史商量工作,却被告知这周轮到行政部的小郑了,而去找小郑的时候,被告知市场部也是组织者之一,本周轮到市场部了。


袁 Sir 因为业务繁忙,加上翻来覆去的对接工作,搞得心累,但他又很重视这件事情。琢磨良久,他决定给自己招一个读书会大使(有钱了可以稍微任性一下下),每周要开展读书运动大会前,他直接跟读书大使对接,大使后续再去协调对应的部门开展工作。


经过这么一整改,袁 Sir 得到了解放,再也不用关心这周该由哪个部门组织,下次会有什么新的部门加入进来。


看着读书运动在公司里遍地开花,袁 Sir 心想着年底了要给读书最多,分享最多的同事多发 12 个月的年终奖…


[OCP 解读] 在一开始,袁 Sir 支持某个部门举办的读书会,后续只要有新的部门要负责举办时(好比功能上的扩展),袁 Sir 都要改变自己的沟通方式,这意味着他自身做出修改才能支持新的需求,此时,袁 Sir 对修改是开放的。引入读书大使后,相当于做了一个抽象,袁 Sir 只需要依赖这个抽象,再有新的部门举办读书会时,他就不需要在改变沟通方式了,此时,袁 Sir 对修改是关闭的,对扩展是开放的。


在软件设计中,OCP 提倡对修改关闭,对扩展开放。建议为你的服务调用者提供一个他需要的抽象、高层次的行为接口,后期你的服务有新的种类,你只需要新增一个实现该抽象、高层次接口具体服务,而不需要修改调用者的使用方式。


创业故事回顾


从袁 Sir 的故事中可以看出来,袁 Sir 在不同阶段之所以面临各种不同的痛点,主要原因有两点:


  1. 关注的事情太多

  2. 关注事情的细节


创业初期,袁 Sir 这么做问题不大,公司在不断地发展壮大(如同软件的演变),袁 Sir 就需要琢磨出新的方案,而这些方案的核心观念无非两个:


  1. 分离关注点

  2. 引入中间人


在面向对象软件设计中,关注点分离,其实体现的就是软件设计的精髓 – 高内聚,低耦合,引入一个中间人 则跟 面向抽象编程 有异曲同工之处。


写在最后


SOLID 原则,它其实是在帮助指导我们设计出高内聚,低耦合 的软件,降低软件后期的维护成本。虽然原则不能时刻有效指导编码落地,理解这些原则背后的设计理念,让你迈出了第一步,接下来,你需要做的是在前进的路上,不断地进行编码实践、思考总结,将其内化。


最后,我想跟技术人员共享一个关键词汇 – 用户视角/业务视角:


我们学到了的很多技巧(设计原则、设计模式、重构手法等),如果忘记初衷,失去目标,代码写得再炫酷、再健壮,也很难体现出应有的价值。从业务需求出发,以用户视角看问题,致力编写出简洁可用的代码即可。


文章转载自:ThoughtWorks 洞见(ID:TW-Insights)

原文链接:你懂SOLID原则吗?

2020-12-25 08:001616

评论

发布
暂无评论
发现更多内容

国内首个开源物联网边缘工业协议网关软件,Neuron v2.0产品解读

EMQ映云科技

开源 物联网 IoT 5月月更 neuron

DTMO直播预告|Taier1.1新功能详解&控制台介绍

袋鼠云数栈

大数据

清晰明了!人人都能懂的Python自动发送邮件实战教程

Python全栈库

Python 编程 程序员 面试 全栈开发

JAVA OOM异常可观测最佳实践

观测云

可观测性 可观测

漏洞扫描器并非100%靠谱,那么容器镜像安全又当如何保证?

青藤云安全

网络安全 安全管理 漏洞修复

邀您填写调研问卷 | 2022中国 AIOps 现状调查全面启动!

博睿数据

AIOPS 博睿数据

《安全大讲堂》 第十四期|不破不立:软件供应链的威胁与方案

腾讯安全云鼎实验室

供应链 安全大讲堂

三、云原生安全关键要素

穿过生命散发芬芳

云原生安全 5月月更

FinClip+系列 | VUE前端开发框架核心原理

Speedoooo

Vue 前端框架 移动开发 移动端开发 小程序容器

接口测试工具简介!

Liam

测试 自动化测试 测试工具 测试自动化 测试管理工具

6 月亚马逊云科技培训与认证课程,精彩不容错过!

亚马逊云科技 (Amazon Web Services)

架构师 培训 认证

vuejs中的mixin混入-局部混入/全局混入

itclanCoder

Vue 前端开发

工业质检如何以“智”取胜?15分钟上手工业零部件检测全流程方案

百度开发者中心

小白福利!教你用低代码实现一个简单的HarmonyOS页面跳转功能

HarmonyOS开发者

HarmonyOS 低代码开发

vuejs中的普通方法/计算属性computed与监听属性watch四者的比较

itclanCoder

JavaScript Vue 前端开发

Hoo研究院 | 什么是流动性池?(下)流动性池的运作

区块链前沿News

defi 流动性 Hoo

【LeetCode】后继者Java题解

Albert

LeetCode 5月月更

Dubbo3 落地实践及 Mesh 解决方案

阿里巴巴中间件

阿里云 开源 云原生 dubbo 中间件

SeaTunnel 加入开源之夏!一起来拿奖金

Apache SeaTunnel

Apache 大数据 开源 workflow Seatunnel

攻防演练中常见的8种攻击方式及应对指南

青藤云安全

vuejs中的默认插槽-具名插槽-作用域插槽三者的比较

itclanCoder

JavaScript Vue 前端开发

大咖说*图书分享-Node布道师狼叔|三卷书详解Node.js

大咖说

前端 后端 代码

Cocos 常用功能介绍

空城机

Cocos 5月月更

netty系列之:HashedWheelTimer一种定时器的高效实现

程序那些事

Java Netty 程序那些事 5月月更

时间轮算法

领创集团Advance Intelligence Group

算法 时间轮算法

毕设不会做,怎么办?

图灵教育

机器学习 深度学习 毕设

Spark离线开发框架设计与实现

百度开发者中心

明明已部署EDR,服务器为什么还是被入侵了?

青藤云安全

安全攻防 网络安全 主机安全

一文彻悟容器网络通信

阿里巴巴中间件

阿里云 容器 云原生 中间件

开源生态在中国:播撒种子,待成雨林

科技热闻

这些年,使用缓存踩过的坑

鲸品堂

缓存

你懂SOLID原则吗?_语言 & 开发_张凯峰_InfoQ精选文章