Soft_Job 板


LINE

大家好,小弟一直觉得 OOP 很困难、设计类别很困难。 我一直想找一个比较量化分析的方式,在工作时辅助设计类别。 於是我设计了一个简单的数学公式 跟大家分享一下这个公式,谢谢大家 网页好读版: https://devs.tw/post/340 --- 正文开始 --- 我一直觉得 OOP 很困难、设计类别很困难。 需要根据经验、遵守一些原则才能写出比较好的程式码。 产出的程式码也只有其他 senior 看得懂为什麽优质,junior 连分辨好坏都不容易。 也就是大家只能依靠质化分析,用文字描述为什麽好、为什麽坏。 我一直想找一个比较量化分析的方式,在工作时辅助设计类别。 於是我设计了一个简单的数学公式,今天跟大家分享一下。 # 内聚系数 (Cohesion Ratio) 要替 OOP 类别打分数听起来有点荒唐,但我设计了一个简单的量化分析方法。 我把这个量化分析算出来的值称为「内聚系数」,英文叫做 Cohesion Ratio(下文以 CR 代称)。 CR 的计算方式非常简单,就是一个类别里面: ``` (每个方法用到的属性数量加总)/(属性的数量 * 方法的数量) ``` 举例来说,如果今天有一个类别长这样: ``` class Apple { private $propertyX; private $propertyY; private $propertyZ; function methodA() { // blah } function methodB() { // blah } function methodC() { // blah } function methodD() { // blah } } ``` 那麽这个类别属性的数量是3,方法的数量是4。所以 CR 的分母就是 `3 * 4 = 12` 如果 methodA 用到了 propertyX,methodB 用到了 propertyX 跟 propertyY,methodC 用到所有属性,methodD 没有使用到任何属性,那麽 CR 的分子就是 `1 + 2 + 3 + 0 = 6` 便可以算出来 CR 就是 `6 / 12 = 0.5` 如果每个方法都用到了所有属性,那麽 CR 就会是 `(3 + 3 + 3 + 3) / 12 = 1` 如果每个方法都没有用到任何属性,那麽 CR 就会是 `(0 + 0 + 0 + 0) / 12 = 0` 你可以用这个公式帮任何一个类别打分数,分数越高,代表它是一个越好的类别! CR 最高就是 1,最低就是 0 实务上大概不可能写出 CR = 1 的类别,但要避免写出 CR 接近 0 的类别。总之 CR 越 高越好。 接着让我使用这个「内聚系数」来评论、量化分析一些开发情境吧! # God Object 上帝物件 这是一个知名的 anti-pattern,也就是一个类别负责了过多任务,变成极为肥大的类别 。 这种类别的属性很多、方法也很多,而且大部份的方法都只用到少数几个属性。 根据内聚系数的公式,你会发现这种类别的 CR 一定非常低,非常接近0 该如何改善呢?根据内聚系数的公式,你会发现:把类别拆小就可以了。 把相关的 property 跟 method 放在一起,很轻易就能拆出多个 CR 更高的类别。 当你这麽做的同时,通常很神奇地,类别命名会自然浮现。 # Long Method 过长方法 有时候你已尽力用上所有最佳原则、遵循 SOLID 指引,但还是有些 method 很长。 理性上你觉得这太长了,需要改写,但感性上又觉得它们的确是一整串连续整体的逻辑。 该怎麽办呢? 像这样的 code,里面出现的变数一定很多,每段任务分别用到部份的变数。 你可以分两个步骤改写: 步骤一、先把每段任务拆成各自的多个小 method,把用到的变数传进去。 你会发现同样的好几组变数,反覆传到好几个 method,似乎让 code 变得更糟糕了! 步骤二、把这些反覆在传的变数,变成类别的 property。 你会发现很多 method 不再需要依靠参数,直接跟 property 互动即可。 并且因为这些 property 反覆在多个 method 被使用,这时 CR 也跟着提高了! 很简单吧!改写之後更可读、更好维护,轻松改善 long method 的问题! 话说回来,那把出现的所有变数都变成 property 如何? 你会发现有些变数只在一两个 method 被使用,提昇为 property 反而会导致 CR 下降! 内聚系数的量化分析明确告诉你:这样不太好! # 基於 Active Record Pattern 的 model 虽然一大堆 web 框架预设使用 Active Record Pattern 作为 domain modeling 的方式 ,但你会发现这方式问题还不小。 相关的 class 属性几乎被绑定成跟资料库 table 的栏位一样。 本文提到的利用 CR 协助类别设计的技巧就很难帮上忙。 这也是 Active Record Pattern 评价两极的主要原因之一。 所以 google keyword: active record pattern anti oop 才会有一堆相关文章。 # Service Object 那些基於 Active Record Pattern 进行 domain modeling 的社群因此会提倡 service objects 的使用。 这些 service object 类别通常都没有 property,只有一些把 entity 作为参数、纯粹 描述商业逻辑的 method。 虽然是把 code 拆分到多个类别了,但你会发现,CR 的分子分母都是 0,这种类别有内 聚性可言吗? 好像不该写出这些类别?但我觉得这些 service object 很好用阿,怎麽办? 其实 OOP 讲究把相关的资料跟行为放在一起,像这种「单纯描述行为」的类别,从 OOP 的角度来看的确很差劲。 这些类别给人 Procedural Programming 的感觉似乎更多一些。 所以 google keyword: service object anti oop 才会有一堆相关文章。 其实也没关系,反正现代程式设计本来就是各种 Programming Paradigm 都掺杂了一些。 如果说一定要从 OOP 的角度去反省,那你不妨这样去想: > 这些 business logic 我急着要上线使用,暂时不知道属於 domain modeling 的哪里 ,所以没有内聚性可言,暂时都先做成 service object 摆着。未来当我发现某些参数重 复出现在一些 service object 时,我可以再把他们一起重写成一个独立的类别,到时这 些类别就会有不错的 CR 值了! 通常到了那个时候,公司更确定自己的经营方向、工程师更确定 domain modeling 该怎 麽设计,所以 CR 自然会通通提昇!到时你做的类别命名也不再是 XXXService 、 XXXManager 这种含糊结尾,你会很容易找到一个很满意的名称! 目前这种 CR = 0/0 的类别,就当作是一种权宜之计吧! 话说回来,既然这些 CR = 0/0 的类别已经不太 OOP 了,你大可发挥创意,把这些商业 逻辑放在你爽的地方就好。 - 可以利用程式语言的 trait/mixin 特性来写这些纯行为的逻辑。 - 直接开一个档案里面只有一堆 function 根本不写 class 也可以。 - 找个类别写一大堆「静态方法」也可以。 反正本来就没有内聚性可言,只是找个地方写的权宜之计,还有很多 service object 之 外的作法。 可以当成是进行到一半的 domain modeling,未来有机会再完成领域建模吧! (其实零除以零的结果并不是零,所以 CR 在此处也没有否定 service object。它只是 说:我无言了) # 结论 OOP 程式设计易学难精,本文提到的内聚系数与量化分析只是一种辅助工具。 经典的各种程式设计原则、SOLID 原则、设计模式,都还是需要花时间乖乖学习。 实务上也没有什麽真的 CR = 0.6 以上才算及格之类的数字,CR 数字大小终究是另一个 依赖经验的主观问题。 我个人通常在设计类别实在很卡关、实在很赶时间的时候,才会拿内聚系数来思考一下。 因为它很无脑简单,就是属性跟方法拆一拆、搬一搬而已,相关的命名会自然浮现。 有机会你也可以试试看,用内聚系数帮你的类别们做个简单的量化分析。 顺利提高系数的时候,真的会有一种感觉:我的类别内聚性还不错哦! --- 正文结束 --- 最後顺带一提 https://devs.tw 是我开发用来给工程师 写笔记、分享技术文章的论坛 支援 Markdown 格式,欢迎一起加入写作与分享的行列,谢谢! --



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 220.129.63.2 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Soft_Job/M.1589892032.A.94D.html
1F:推 neo5277: 总觉得这有点把简单的东西复杂化了不过有心给推 05/19 20:46
2F:推 DarkIllusion: 昨天在脸书python社群有看到 不错 05/19 20:58
3F:推 chuegou: 觉得有趣 05/19 21:37
4F:推 jerryshadow: 很酷 05/19 21:39
5F:→ JasperChang: 觉得会走向跟KPI一样的结局 05/19 22:23
6F:推 yamakazi: https://i.imgur.com/v0fqqdO.jpg 05/19 23:28
7F:推 Masakiad: 这个东西跟架构还差太远了吧...... 05/19 23:29
8F:嘘 bibo9901: 浓浓民科风 想当码农务必详读 05/19 23:34
9F:推 backprog: 很棒啊可以先去投研讨会论文 05/19 23:58
10F:嘘 as30385438: 觉得OOP难不去搞懂而搞这套只会让架构更烂而已 05/20 00:29
11F:推 seLain: Bansiya02-QualityModel 入门paper 给有兴趣的人 05/20 08:39
12F:→ ohohoh: 可以google参考一下,CBO,coupling between object,概念 05/20 11:41
13F:→ ohohoh: 很像 05/20 11:41
14F:推 wulouise: 你想做的是Cyclomatic complexity?我觉得可以当作参考 05/20 12:10
15F:→ ohohoh: Cyclomatic complexity针对一个函数内,原文要做是class间 05/20 12:32
16F:→ ohohoh: 公司在用understand这软体,可参考一下他可提供的metrics 05/20 12:33
17F:→ ohohoh: 但我同意回文那篇,算数字像是硬做KPI 05/20 12:36
18F:推 Jichang: 感觉非常奇怪的算法 method越多分数越低 全部写在main里 05/20 18:51
19F:→ Jichang: 面 会拿到满分 05/20 18:51
20F:推 maik060: 很有趣, 要是有这工具我肯定用 05/21 17:45







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:Gossiping站内搜寻

TOP