专栏首页DDD应对变化

应对变化

之前对SOLID做了一个总结 《SOLID》总结[1]

这些原则是前辈们经过无数实践提炼出来的,百炼成刚,那是不是成了放之四海皆准的道理呢?某种程度上讲,还真就是准的,常被人耳提面命写的代码要遵守这些原则,想想code review时,是不是代码常常对比这些原则,被人指出没有遵循哪个原则

总结篇中画了这幅图,SOLID也的确是我们达到高内聚低耦合很重要的手段

//读取配置文件和计算
class Computer{
    public int add() throws NumberFormatException, IOException {
        File file = new File("D:/data.txt");
        BufferedReader br = new BufferedReader(new FileReader(file));
        int a = Integer.valueOf(br.readLine());
        int b = Integer.valueOf(br.readLine());
        return a+b;
    }
}

这是一段被用来演示SRP的反例,从示例代码中看出,这个方法职责的确不单一,引起它变化的至少有两个地方:一是数据不一定从配置文件读取、二是计算方式可能会变

在code review时,不管是自己还是别人,的确让人觉得不够完美

因此,我们会花一番功夫,来让方法达到SOLID的要求,可如果此方法从系统上线运行几个月,甚至几年都无需变动,那我们花费的这些时间也只是自我感动,毕竟我们最终目标是给客户交付带来价值的系统,以最快的速度给公司带来效益

这其实是成本的问题,没错,程序员要有技术追求,但也得考虑成本

可总不能为了成本,忽略一切吧,那怎么处理呢,我们要达到“高内聚、低耦合”,SOLID是重要路径,但又不能不计成本地进行SOLID,更不能为了SOLID而SOLID

所以不能走两个极端,既不能坐视不管,也不能一味求全,在这两层之间应该还有一片灰色地带


这灰色地带是什么样的?怎么做才能不去穷举变化疲惫应对,而当真正变化来临时又能轻松应对?大佬提供了思路,不能以这些原则去应对软件变化,应该以无法为有法,以无限为有限。以实际需求变化来帮助我们识别变化,管理变化。这思路就是袁英杰提出的正交设计,有四大策略

四大策略

策略一:消除重复

重复代码,不管接手老项目还是住持新项目,都特别重视重复代码的比率,为什么呢?

1.重复代码增加维护成本,变动同一个逻辑会遗漏修改2.重复代码说明团队沟通不畅,团员间没有交流或者没有必要的code review

这只是实践带来的观察,那么从理论角度说说消除重复的重要性

“重复”极度违背高内聚、低耦合原则,从而会大幅提升软件的长期维护成本;而我们所求的高内聚是指关联紧密的事物放在一起,两段完全相同的代码关联最为紧密,重复就意味着低内聚

更糟糕的是,本质重复的代码,都在表达同一项知识。如果他们表达(即依赖)的知识发生了变化,这些重复代码都需要修改,因而重复代码也意味着高耦合

当完全重复的代码进行消除,会让系统更加高内聚、低耦合

小到代码块,大到模块也一样,如果两个模块之间部分重复,那么这两个模块都至少存在两个变化原因或两重职责;站在系统的角度看,它们之间有重复部分,也有差异部分,那这两个模块都存在两个变化原因

对于这一类型重复,比较典型的情况有两种:实现重复和客户重复

这个策略非常明确,极具操作性,消除重复后,明显提高系统内聚性,降低耦合性,在消除重复过程中,也提高了系统的可重用性,而且对于客户重复,还提高了扩展性

策略二:分离不同的变化方向

对于策略一使用时机,可以随时进行,重复也容易判定。除重复代码外,另一个驱动系统朝向高内聚方向演进的信号是:我们经常需要因为同一类原因,修改某个模块。而这个模块的其它部分却保持不变

分离不同变化方向,目标在于提高内聚度。因为多个变化方向,意味着一个模块存在多重职责。将不同的变化方向进行分离,也意味着各个变化职责的单一化

对于变化方向分离,也得到了另外一个我们追求的目标:可扩展性

策略二相对策略一,最重要的就是时机,不然就会回到我们文章开头时的窘境:早了,过度设计;晚了,则被再次愚弄

当你发现需求导致一个变化方向出现时,将其从原有的设计中分离出去。此时时机刚刚好,不早不晚;Uncle Bob也曾给出答案:被第一颗子弹击中时,也就是当第一个变化方向出现时

这个世界里,本质上只存在三个数字:0,1,和N。

0意味着当一个需求还没有出现时,我们就不应该在系统中编写一行针对它的代码。

1意味着某种需求已经出现,我们只需要使用最简单的手段来实现它,无需考虑任何变化。

N则意味着,需求开始在某个方向开始变化,其次数可能是2,3,...N。但不管最终的次数是多少,你总应该在由1变为2时就需要解决此方向的变化。随后,无论最终N为何值,你都可以稳坐钓鱼台,通过一个个扩展来满足需求

如果我们足够细心,会发现策略消除重复和分离不同变化方向是两个高度相似和关联的策略:

它们都是关注于如何对原有模块进行拆分,以提高系统的内聚性。(虽然同时也往往伴随着耦合度的降低,但这些耦合度的降低都发生在别处,并未触及该如何定义API以降低客户与API之间耦合度)。

另外,如果两个模块有部分代码是重复的,往往意味着不同变化方向。

尽管如此,我们依然需要两个不同的策略。这是因为:变化方向,并不总是以重复代码的形式出现的(其典型症状是散弹式修改,或者if-else、switch-case、模式匹配);尽管其背后往往存在一个以重复代码形式表现的等价形式(这也是为何copy-paste-modify如此流行的原因)。

策略三:缩小依赖范围

前面两个策略解决了软件单元如何划分问题,现在需要关注合的问题:模块之间的粘合点API的定义

•首先,客户和实现模块的数量,会对耦合度产生重大的影响。它们数量越多,意味着 API 变更的成本越高,越需要花更大的精力来仔细斟酌。•其次,对于影响面大的API(也意味着耦合度高),需要使用更加弹性的API定义框架,以有利于向前兼容性。

因此缩小依赖范围,就是要精简API

1.API包含尽可能小的知识。因为任何一项知识的变化都会导致双方变化2.API也要高内聚,不应强迫API的客户依赖不需要的东西

策略四:向稳定的方向依赖

虽然缩小依赖范围,但终究还是要有依赖范围,还是必然存在耦合点。降低耦合度已到尽头。

耦合最大的问题在于:耦合点的变化,会导致依赖方跟着变化。这儿意味着如果耦合点不变,那依赖方也不会变化。换句话说,耦合点越稳定,依赖方受耦合变化影响的概率就越低

因此得出第四个策略:向稳定的方向依赖

耦合点也就是API,什么样的API更侧向于稳定?站在What,而不是 How 的角度;即站在需求的角度,而不是实现方式的角度定义API;也就是站在客户的角度,思考用户的本质需要,由此来定义API,而不是站在技术实现的方便程度角度来思考API定义

SOLID

一个好的面向对象设计,自然是符合高内聚,低耦合原则的对象划分和协作方式。

单一职责和开放封闭,更多的在强调类划分时的高内聚;而里氏替换,依赖倒置,接口隔离则更多的强调类与类之间协作接口(即API)定义的低耦合

单一职责,通过对变化原因的识别,将一个承担多重职责的类,不断分割为更小的,只具备单一变化原因的类。而单一变化原因指的是:一个变化,会引起整个类都发生变化。只有关联极其紧密的情况,才会导致这样的局面。因而,单一职责和高内聚某种程度是同义词。

但单一职责原则本身,并没有明确指示我们该如何判定一个类属于单一职责的,以及如何达到单一职责的状态。而策略消除重复,分离不同变化方向,正是让类达到单一职责的策略与途径

开放封闭原则,正是通过将不同变化方向进行分离,从而达到对于已经出现的变化方向,对于修改是封闭的,对于扩展是开放的

里氏替换原则强调的是,一个子类不应该破坏其父类与客户之间的契约。唯有如此,才能保证:客户与其父类所暴露的接口(即API)所产生的依赖关系是稳定的。子类只应该成为隐藏在API背后的某种具体实现方式。

依赖倒置原则则强调:为了让依赖关系是稳定的,不应该由实现侧根据自己的技术实现方式定义接口,然后强迫上层(即客户)依赖这种不稳定的API定义,而是应该站在上层(即客户)的角度去定义API(正所谓依赖倒置)

但是,虽然接口由上层定义,但最终接口的实现却依然由下层完成,因此依赖倒置描述为:上层不依赖下层,下层也不依赖上层,双方共同依赖于抽象。

最后,接口隔离原则强调的是:不应该强迫客户依赖它不需要的东西。显然,这是缩小依赖范围策略在面向对象范式下的产物

总结

尽管理论上讲,任意复杂的系统都可以被放入同一个函数里。但随着软件越来复杂,即便是智商最为发达的程序员也发现,单一过程的复杂度已经超出他的掌控极限。这逼迫人们必须对大问题进行分解,分而治之,这也是必须模块化的原因

模块化主要是两方面:

1.软件模块该如何划分?(怎么分)2.模块间API该如何定义?(怎么合)

本文四个策略,前两个指导怎么高内聚,也就是怎么分;后两个指导耦合方式,怎么合

重要的是使用各个策略的使用时机,变化驱动识别变化、重构变化

变化导致的修改有两类:

•一个变化导致多处修改(重复);•多个变化导致一处修改(多个变化方向);

由此得到前两个策略:消除重复;分离不同变化方向。

除此之外,我们要努力消除变化发生时不必要的修改,也有两种方式:

•不依赖不必要的依赖;•不依赖不稳定的依赖;

这就是后面两个策略:缩小依赖范围,向着稳定的方向依赖。

References

[1] 《SOLID》总结: http://www.zhuxingsheng.com/blog/solid-summary.html [2] 变化驱动:正交设计: https://www.jianshu.com/p/d127b8afc8cb

本文分享自微信公众号 - 码农戏码(coder-game),作者:朱先生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-07-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何应对变化?

    当你的一个项目数据库都定下来后,而且已经开发了若干个工作日,突然接到甲方公司提出,某个功能要改变,原先的需求分析要重新改,如果这个修改是涉及的数据库的表结构更改...

    PM吃瓜
  • 云计算需要快速应对变化

    云计算的广泛应用已经表明人们是否会接受不再是一个问题,而是如何更好地采用。为了跟上21世纪的发展步伐,企业需要仔细考虑如何最好地将云计算集成到其系统和供应链中。

    静一
  • “设计应对变化”--实例讲解一个数据同步系统

     系列文章索引: [WCF邮件通信系统应用 之 数据同步程序 之 设计内幕 之 一] 同步一个数据库要发多少个数据包? [WCF邮件通信系统应用 之 数据同步...

    用户1177503
  • 用机器学习应对气候变化?

    全球变暖、冰川消融、海平面上升、极端天气事件频发……人类的生存正在逐渐受到威胁。科学家们已经从很多不同的角度对气候变化进行了深入的研究,提出了很多应对气候变化的...

    zhangqibot
  • 如何打造顺畅的开发流程——应对需求变化

    破解软件项目管理难题,从改变看待问题的方式开始。开发流程根据不同的项目应有不同的变化,但是团队中每个角色的责任应该是相对固定的。 一 既然屁股决定大脑,就让屁股...

    韩伟
  • 绿色建筑真的可以帮助应对气候变化吗?

    目前的气候状况充令人担忧。全球平均温度升高,雪和冰层减少,海平面上升。 除非采取重大行动抑制温室气体排放,否则没有迹象表明这些趋势会放缓。实际上,联合国呼吁各国...

    用户4122690
  • Gartner解读:如何应对不断变化的网络安全需求

    当一开始担任一家零售企业的信息安全管理者时,处理IT安全问题还是相对简单的。但是随着社会的发展,传统行业逐渐向数字经济、云平台、物联网靠拢,以支持企业的数字化商...

    FB客服
  • Python监视域名对应IP地址变化情况

    为了负载均衡或者增加黑客攻击难度,很多域名对应的IP地址是会经常变化的。 from time import sleep from socket import g...

    Python小屋屋主
  • 吴恩达、Jeff Dean、Bengio对话:如何用机器学习应对气候变化 | NeurIPS 2019

    在今年的NeurIPS会议上,机器学习大神们聚集在一起,讨论了人工智能如何应对气候变化对地球生命的影响。

    量子位
  • 运用适配器模式应对项目中的变化

    作者:张纪刚 链接: http://blog.csdn.net/zhangjg_blog/article/details/18779607 (点击文末阅读原文...

    java达人
  • CVPR 2020 | 商汤提出SEPC:应对尺度变化的目标检测新算法

    在CVPR 2020上,为了更好的解决物体检测中的尺度问题,商汤EIG算法中台团队重新设计了经典的单阶段检测器的FPN【1】以及HEAD结构,通过构造更具等变性...

    AI算法修炼营
  • 物联网和数据科学用于应对气候变化的4种方式

    技术是变革的强大催化剂。碳捕集技术,大数据,人工智能和其他物联网趋势使团体能够了解和应对气候变化。

    用户4122690
  • SaaS公司从短期、中期到长期,如何应对环境的变化

    ? 本文作者 吴昊:腾讯SaaS加速器导师、SaaS战略及营销顾问,具有20年企业信息化和6年SaaS营销团队创新经验。 “新冠疫情”爆发前,我正巧在美国加...

    腾讯SaaS加速器
  • 专访瑞数信息吴剑刚:River Security,用变化应对未知

    作为世界第二大经济体,我国有百余家企业位列全球五百强,这诱使黑客一次次地厮杀,瓜分利益的蛋糕。同时,移动互联的大面积覆盖催生了各行各业的进入,信息再次指数级增长...

    FB客服
  • 应对全球气候变化新绝招?人类开始给牛戴口罩了

    全世界的农场中蓄养着 16 亿头牛,这种反刍动物经常打嗝放屁,不停释放甲烷。无色无味的甲烷与二氧化碳同样都是温室气体,但是对于全球变暖的“贡献”却比后者高出 8...

    气象学家
  • 为应对大环境变化,华为正重塑供应链,希望将国外供应商产能转移至国内

    相关知情人士表示,当前华为有部分芯片的生产是在国外完成的,为此该公司希望国外供应商能够增加在中国的产能,尤其是封测等,并大力支持中国供应商扩产。

    镁客网
  • C11与C99的变化对比

    C11标准是C语言标准的第三个版本,前一个标准版本是C99标准。2011年12月8日,国际标准化组织(ISO)和国际电工委员会

    用户7886150
  • iOS SwiftyJSON 对应的JSON 转化为 对象

    SwiftyJSON确实很好用 不会因为取了某个空对象的值而导致程序的崩溃 但是 一直这样data["a"]["b"]["c"].stringValue的形式也...

    剑行者
  • 如何应对甲方的需求变更?

    如何应对甲方的需求变更?应对方法是拒绝需求变更吗?你能否区分它是真的是需求变更吗?你看过一本书叫做《火球 - uml大战需求分析》吗?

    张晓衡

扫码关注云+社区

领取腾讯云代金券