奥卡姆剃刀:让事情回归简单
最近,阿里云的同学告诉我,COLA作为应用架构,已经被选入阿里云的Java应用初始化的应用架构选项之一。
This is really something,于是,在这个里程碑节点上,我开始回过头来,重新审视COLA一路走来的得与失。
COLA作为一种架构思想无疑是成功的。但是作为框架,个人感觉有点鸡肋之嫌。 特别是在简洁性上做的不好,感觉做了不少画蛇添足的事情。
试想一下,有些功能我作为作者都很少去使用,我实在想不到,它为什么还有存在的理由。
基于上面的思考,我做了这一次COLA 2.0 到 COLA 3.0的升级。在本次升级中,我没有增加任何新的功能,而是尽量多删减了一些概念和功能。让COLA可以更加纯粹的focus在应用架构上,而不是框架支持和架构约束上。
支持我做这些决策的背后原因只有一个——奥卡姆剃刀原理。
奥卡姆剃刀原理
奥卡姆剃刀原理,是指如无必要,勿增实体(Entities should not be multiplied unnecessarily),即“简单有效原理”。正如奥卡姆在《箴言书注》2卷15题说“切勿浪费较多东西去做,用较少的东西,同样可以做好的事情。”
在具体的应用过程中,我们可以遵循以下原则去做事情:“如果同一个现象有n种理论,最简单的那个便是最正确的。能用n做好事情,那就不要有第n+1个动作。”
比如,《皇帝的新衣》的皇帝到底穿没穿衣服呢?如果你在现场,你很有可能就是大臣之一。
如果懂得了奥卡姆剃刀原理,可以用逻辑手段,判断谁是真理。
第一种逻辑如下:假设皇帝是真的穿了衣服→假设愚蠢的人看不见→假设你就是愚蠢的人→所以你没看见皇帝穿衣服。
第二种逻辑如下:假设皇帝没穿衣服→所以你没看见皇帝穿衣服。
同样看见光身子的皇帝,第二种解释简单明了。而第一种解释,可能正因为它是错误的,就需要更多假设来补救漏洞,就像说谎圆谎一样。
真相不需要伪装掩饰,简单明了。
再比如,地心说和日心说,托勒密的地心说模型是一个本轮均轮模型。人们可以按照这个模型,定量计算行星的运动,据此推测行星所在的位置。
到了中世纪后期随着观察仪器的不断改进,人们能够更加精确地测量出行星的位置和运动,观测到行星实际位置与这个模型的计算结果存在偏差,一开始还能勉强应付,后来小本轮增加到八十多个,却仍然不能精确地计算出行星的准确位置。
1543年,波兰天文学家哥白尼在临终时发表了一部具有历史意义的著作—《天体运行论》。这个理论体系提出了一个明确的观点:太阳是宇宙的中心,一切行星都在围绕太阳旋转。该理论认为,地球也是行星之一,它一方面像陀螺一样自转,一方面又和其他行星一样围绕太阳转动。
哥白尼的计算不仅结构严谨,而且计算简单,与已经加到八十余个圈的地心说相比,哥白尼的计算与实际观测资料能更好地吻合。因此,地心说最终被日心说所取代。
设计中的弯弯绕
深入考察一下,我们系统中,类似于“地心说”这样的弯弯绕的设计,实在是不在少数。
从系统架构的角度看,有些弯弯绕是因为系统边界划分不合理,导致职责不清,依赖混乱。
从应用架构的角度看,有些弯弯绕是因为过度设计,为了追求所谓的灵活性和可扩展性,使用了不恰当的设计。导致本来可以直观呈现的代码逻辑,被各种包装,各种隐藏,各种转发.... 无形中极大的阻碍了代码的可读性和可理解性,增加了维护成本。
举个例子,我看过无数的业务系统,喜欢拿业务流程编排说事情。因此,在业务系统中,可以看到各种五花八门的“弯弯绕设计”。
比如,在一个业务系统中,我看到了如下的pipeline设计。这个设计的本质是说把一个复杂的业务操作进行结构化拆解为多个小的处理单元。
拆解是正确的,但是这种处理方式显然不够“奥卡姆”(关于更多结构化分解的内容,可以看我的另一篇文章)。作为维护人员,进入“入口函数”后,还要去查数据库,然后才能知道哪些组件被调用了,太绕了,不够直观,也不简洁。
同样的逻辑,按照下面的方式写不香吗?
public class CreateCSPUExecutor {@Resourceprivate InitContextStep initContextStep;@Resourceprivate CheckRequiredParamStep checkRequiredParamStep;@Resourceprivate CheckUnitStep checkUnitStep;@Resourceprivate CheckExpiringDateStep checkExpiringDateStep;@Resourceprivate CheckBarCodeStep checkBarCodeStep;@Resourceprivate CheckBarCodeImgStep checkBarCodeImgStep;@Resourceprivate CheckBrandCategoryStep checkBrandCategoryStep;@Resourceprivate CheckProductDetailStep checkProductDetailStep;@Resourceprivate CheckSpecImgStep checkSpecImgStep;@Resourceprivate CreateCSPUStep createCSPUStep;@Resourceprivate CreateCSPULogStep createCSPULogStep;@Resourceprivate SendCSPUCreatedEventStep sendCSPUCreatedEventStep;public Long create(MyCspuSaveParam myCspuSaveParam){checkRequiredParamStep.check(context);checkUnitStep.check(context);checkExpiringDateStep.check(context);checkBarCodeStep.check(context);checkBarCodeImgStep.check(context);checkBrandCategoryStep.check(context);checkProductDetailStep.check(context);checkSpecImgStep.check(context);createCSPUStep.create(context);createCSPULogStep.log(context);sendCSPUCreatedEventStep.sendEvent(context);return context.getCspu().getId();}
}
这种写法简单直观,易维护,与前一种方式相比,具有同样的组件复用性。符合奥卡姆剃刀的精神,相比较而言,前面那种弯弯绕设计,虽然看起来有点设计感,带来了一点点OCP的好处。但是无端增加了理解和认知成本,孰优孰劣,不难分辨。
COLA 3.0 升级
做了这么长的铺垫,终于到了批斗COLA中“弯弯绕设计”的时候了。
去掉Command
在COLA的初始阶段,因为受到CQRS的影响,于是想到了使用命令模式来处理用户请求。设计的初衷是想通过框架,一方面强制约束Command和Query的处理方式,另一方面把Service里面的逻辑,强制拆分到CommandExecutor中去,防止Service膨胀过快。
和上面介绍过的pipeline设计类似,这种设计有点绕,不够直观,如下所示:
public class MetricsServiceImpl implements MetricsServiceI{@Autowiredprivate CommandBusI commandBus;@Overridepublic Response addATAMetric(ATAMetricAddCmd cmd) {return commandBus.send(cmd);}@Overridepublic Response addSharingMetric(SharingMetricAddCmd cmd) {return commandBus.send(cmd);}@Overridepublic Response addPatentMetric(PatentMetricAddCmd cmd) {return commandBus.send(cmd);}@Overridepublic Response addPaperMetric(PaperMetricAddCmd cmd) {return commandBus.send(cmd);}
}
看起来还挺干净的,可是ATAMetricAddCmd到底是被哪个Executor处理的呢,不直观。我还要去理解CommandBus,以及CommandBus是如何注册Executor的。无形中增加了认知成本,不好。
既然这样,为何不用奥卡姆剃刀把这个CommandBus剔除呢。如下所示,去除CommandBus之后,代码是不是直观了很多,唯一的损失是我们会失去框架层面提供的Interceptor功能,然而,Interceptor正是我下一个要动刀的地方。
public class MetricsServiceImpl implements MetricsServiceI{@Resourceprivate ATAMetricAddCmdExe ataMetricAddCmdExe;@Resourceprivate SharingMetricAddCmdExe sharingMetricAddCmdExe;@Resourceprivate PatentMetricAddCmdExe patentMetricAddCmdExe;@Resourceprivate PaperMetricAddCmdExe paperMetricAddCmdExe;@Overridepublic Response addATAMetric(ATAMetricAddCmd cmd) {return ataMetricAddCmdExe.execute(cmd);}@Overridepublic Response addSharingMetric(SharingMetricAddCmd cmd) {return sharingMetricAddCmdExe.execute(cmd);}@Overridepublic Response addPatentMetric(PatentMetricAddCmd cmd) {return patentMetricAddCmdExe.execute(cmd);}@Overridepublic Response addPaperMetric(PaperMetricAddCmd cmd) {return paperMetricAddCmdExe.execute(cmd);}
}
去掉Interceptor
当时设计Interceptor,是因为有CommandBus作为基础,为了更好的利用命令模式带来的好处,便添加了Interceptor功能。其本质是一个AOP处理。
鉴于Spring的AOP功能已经很完善了,这个设计也是有点鸡肋。事实证明,大家在使用COLA框架的时候,很少会使用Interceptor,包括我自己也是一样。既然如此,剔除也罢。
去掉Convertor、Validator、Assembler
关于命名的重要性,这里就不赘述了。当时想着是否能从框架层面,规范一下一些常用功能的命名。但是在实际使用中,发现这个想法也是有些过于理想化了。
我记得,在团队实践COLA的初期,还经常为什么是Convertor(转换器),什么是Assembler(组装器)的事情,争论不休。
后面我仔细想了想,命名虽然很重要,但其作用域最多也就是一个团队规范,你校验器是叫Validator还是Checker并没有什么本质区别,团队自己定义就好了。尝试从框架层面去解决团队约定问题,其效果不会太好,因此也果断挥刀剔除。
类扫描优化
业务身份和扩展点的思想,是TMF的核心理念,也是阿里业务中台的进行多业务支持的核心方法论。
COLA致力于提供一种轻量级的扩展实现方式,因此该功能得以在奥卡姆的屠刀下得以保存。因为COLA的扩展点设计是借鉴了中台的TMF,因此在前面的设计中,其类扫描方案是直接照搬TMF的做法。
实际上,TMF的类扫描方案对COLA来说有点多余。因为COLA本身就是架设在Spring的基础之上,而Spring又是建立在类扫描的基础之上。因此,我们完全可以复用Spring的类扫描,没必要自己写一套。
在原生的Spring中,至少有3种方式可以获取到用户自定义Annotation的Bean,最简洁的是通过 ListableBeanFactory.getBeansWithAnnotation
方法,或者使用 ClassPathScanningCandidateComponentProvider
进行扫包。
在这次改版中,我选用的是 getBeansWithAnnotation
方法,主要是为了获取@Extension的Bean,用来实现扩展点功能,废弃了原来的TMF类扫描实现。
总结
触发这次升级的动机,主要是因为,自己在实践COLA的过程中,的确发现有些华而不实的功能。在COLA作为阿里云的基础应用架构,其影响力越来越大的时候,我有责任给到大家一个正确的引导——去伪存真,简洁有效,而不是引入更多的复杂度。
实际上,COLA是有两部分组成的:
一方面COLA是一种架构思想,是整合了洋葱圈架构、适配器架构、DDD、整洁架构、TMF等架构思想的一种应用架构。
在这次升级中,架构思想部分基本没有变化,唯一一点是因为去除了Command概念,因此CQRS也成了可选项,而不再是一种强要求。
另一方面COLA也是框架组件,通过这次升级,我使用奥卡姆剃刀砍掉了绝大部分的组件能力,仅仅保留了扩展点功能。其用意是不希望COLA作为框架给到应用开发者太多的约束,这不符合简单有效的风格。
所以,总结下来,与其说这是一次升级,不如说它是功能“降级”,是在做减法。
但我相信,减法可以让COLA更加符合奥卡姆精神,帮助COLA轻装上阵,走的更远。
COLA开源地址:https://github.com/alibaba/COLA
奥卡姆剃刀:让事情回归简单相关推荐
- 100种思维模型之奥卡姆剃刀思维模型-87
Everything should be made as simple as possible, but not simpler (凡事都应当尽可能地简单,而不是较为简单)--爱因斯坦 ...
- 上交大许志钦:神经网络中的奥卡姆剃刀——简单有效原理
来源: 智源社区 作者:许志钦 整理:熊宇轩 编辑:李梦佳 本文整理自青源Talk第十期,视频回看地址:https://event.baai.ac.cn/activities/217 [专栏:研究思路 ...
- 极简主义的思想内核-奥卡姆剃刀原则
在去年的跨年演讲里,罗胖曾讲过一个"奥卡姆剃刀"原则,当时只觉得新奇,倒没有去仔细研究,直到昨天在一篇文章里又看到这个名词,说的神乎其神,比如下面这段描述: "这把剃刀出 ...
- 奥卡姆剃刀是什么?机器学习实践中那些学习模型或者那些评估指标践行了这一理论?
奥卡姆剃刀是什么?机器学习实践中那些学习模型或者那些评估指标践行了这一理论? 奥卡姆剃刀:无无必要,勿增实体. 奥卡姆剃刀原理应用于模型选择时变为以下想法:在所有可能选择的模型中,能够很好地解释已知数 ...
- L1,L2正则化理解-奥卡姆剃刀(Occam's razor)原理
L0.L1与L2范数转自:zouxy09@qq.com http://blog.csdn.net/zouxy09 今天我们聊聊机器学习中出现的非常频繁的问题:过拟合与规则化.我们先简单的来理解下常用的 ...
- 墨菲定律、二八法则、马太效应、手表定理、“不值得”定律、彼得原理、零和游戏、华盛顿合作规律、酒与污水定律、水桶定律、蘑菇管理原理、钱的问题、奥卡姆剃刀等13条是左右人生的金科玉律。
一.墨菲定律 1949年,一位名叫墨菲的空军上尉工程师,认为他的某位同事是个倒霉蛋,不经意间开了句玩笑:"如果一件事情有可能被弄糟,让他去做就一定会弄糟." 这句话迅速流传,并 ...
- 机器学习学习记录2:归纳偏好(奥卡姆剃刀原则和NFL定理)
定义 对于相同的训练样本,不同学习算法会产生不同的模型,决定其产生模型的,是学习算法本身的"偏好".此处,书中引入"归纳偏好"的概念: 机器学习算法在学习过程中 ...
- 什么是“奥卡姆剃刀”,如何用“奥卡姆剃刀”解决复杂问题?复杂问题简单化
什么是"奥卡姆剃刀",如何用"奥卡姆剃刀"解决复杂问题?复杂问题简单化 问题 什么是"奥卡姆剃刀"? 如何使用"奥卡姆剃刀&quo ...
- 应用架构COLA3.0:让事情回归简单
最近,阿里云的同学告诉我,COLA作为应用架构,已经被选入阿里云的Java应用初始化的应用架构选项之一. This is really something,于是,在这个里程碑节点上,我开始回过头来,重 ...
- 墨菲定律、二八法则、马太效应、手表定理、“不值得”定律、彼得原理、零和游戏、华盛顿合作规律、酒与污水定律、水桶定律、蘑菇管理原理、钱的问题、奥卡姆剃刀等13条是左右人生的金科玉律
转载地址:http://blog.csdn.net/byxdaz/article/details/3981125 墨菲定律.二八法则.马太效应.手表定理."不值得"定律.彼得原理. ...
最新文章
- Myeclipse 10 利用工具生成网络服务接口并调用
- 企业项目构建学习(一)maven
- 开课吧怎么样_开课吧数据产品经理课程包括什么?开课吧培训的怎么样
- linux系统报错01031,在Linux环境下设置 ora-01031:insufficient privileges解决方法总结
- 工作315:uni-修改添加时间的逻辑
- 上午写了一段代码,下午就被开除了~
- 一张图全解析个性化邮件那么重要
- 新型计算机作文1000,科技作文1000字(精选3篇)
- python 缺省参数_week04_python函数缺省值
- pvr转png工具_pngtosvg 一个神奇的在线工具
- 001-keras简介
- plc变频器c语言,PLC控制变频器的几种方法
- 关于阿里云服务器密码重置和使用方法
- [转]基于POI的功能区划分()
- idea设置黑色经典样式Darcula
- 树莓派4B之超声波传感器模块(python3)
- HTML制作注册页面
- 运维工程师优秀员工评语
- AI 能预测夫妻吵架,还会劝你冷静一下
- DS2431 修改网上版本 读写(已测试可用)