55

架构整洁之道导读(一)

 5 years ago
source link: https://lambeta.com/2018/10/28/Guide-to-Clean-Architecture-1/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

我是《架构整洁之道》( Clean Architecture ) 中文版的技术审校者,在审校的过程当中略有感悟,所以希望通过撰写导读的方式分享给大家。

书名的由来

《架构整洁之道》是 Clean Architecture 的中文译名。看似简单地延续了《代码整洁之道》( Clean Code )的翻译传统,但事实上,对于取中文名字这件事,我们还是花了不少气力的。拿到译文初稿时,编辑提供了几个备选的译名:《架构简洁之道》,《架构至洁》和《Clean Architecture》,这些名字各有各的考量,在没有了解这本书的核心思想之前,我也没有办法给出恰当的判断。所以在通读了原作和译作之后,我在ThoughtWorks咨询群里发起提案,讨论的过程很精彩,最终在骨灰级架构师新哥的建议下,结果大致趋向了整洁架构。

新哥说:“整本书在说依赖治理(管理),也就是如果降低依赖复杂度,和DDD中分离子域分层架构等想法是一致的;如同你整理你的房间,把东西分门别类放好,从这个角度,整齐比简单更合适,或者清晰也可。”

除此之外,对于《架构至洁》这个候选项,大魔头的态度是不要至洁,总感觉脏脏的。言下之意,自行体会。而读MBA的岳岳和XR(XR说他没读过MBA)从用户思维出发,认为《代码整洁之道》和《架构整洁之道》可以相互增强记忆,更容易激发用户的购买行为。

即便敲定了“整洁架构”,大家对“之道”也有不同的看法。《代码整洁之道》对应的原标题和副标题分别是 Clean Code - A handbook of Agile Software Craftsmanship ,而《架构整洁之道》对应的原标题和副标题分别是 Clean Architecture - A Craftsman’s Guide to Software Structure and Design 。我们知道“道”是一种形而上的精神层面,老实讲,把 Craftsman (手艺人)译做“道”是有点夸张的。

形而上是精神方面的宏观范畴,用抽象(理性)思维,形而上者道理,起于学,行于理,止于道,故有形而上者谓之道;形而下是物质方面的微观范畴,用具体(感性)思维,形而下者器物,起于教,行于法,止于术,故有形而下者谓之器。

道法术器择其一?其实凡事总有权衡,遵循前人的译法往往不会太坏。就像鲍勃大叔书中总结的稳定依赖原则,当我们依赖一种译法次数越多,它就更加稳定,这种稳定先不说能否形成品牌效应,单是SEO就能省去不少功夫,那么何乐而不为呢?

鲍勃大叔的文字平铺直叙、浅显易懂,尤其喜欢用他自己生活中的经验做例子。而且这本书是没有知识断层的,即便是初级程序员,也能在鲍勃大叔的循循善诱下,完成对软件架构认知的转变。因为他总是从最基础的知识点切入,自下而上,一步步地搭起架构的形状。

范式的实质是约束

编程范式是程序员喜闻乐见的话题,就像Vim和Emacs编辑器地位的旷日之争。它们的沉浮过往俨然就是风云诡谲的江湖。结构化编程英雄迟暮逐渐淡出程序员的视野,觊觎已久的面向对象编程(OOP)以迅雷之势称霸武林,独居一隅的函数式编程(FP)隐忍多年终于等来了一次机会。2012-2014年,江湖唱衰OOP的声音不绝于耳,FP就像一名拯救程序员于水火的侠士想要撼动这片天地。硝烟过后,眼前却不是你死我亡的惨状,而是你中有我、我中有你的大团圆结局。当Java这位OOP的保守党融汇了FP的特性lambda表达式,这场范式的冲突之争也算落下了帷幕。

程序员谈编程范式,喜欢党同伐异,作为FP的拥趸,我也不例外。可是鲍勃大叔却娓娓道来,所谓编程范式不过是约束程序的执行,告诉我们什么不能做而已。

  1. 结构化编程是对程序控制权的直接转移的规范和限制
  2. 面向对象编程是对程序控制权的间接转移的规范和限制
  3. 函数式编程是对程序赋值操作的规范和限制

Goto considered harmful

MrYbuqU.jpg!web

学习C语言编程的第一天,老师就告诉我们不要在程序中使用 goto 语句,因为 goto 会破坏程序的结构化。Dijkstra在论文 Go To Statement Considered Harmful 中证明了 goto 语句阻止了将大程序递归分解成更小的可证明的单元,这意味着大量使用 goto 语句的程序是不能被证明的。这里,不能被证明的语义是不可判定,类似说谎者悖论——“我在说谎”这句话不能被证明和证伪,所以不用 goto 其实是在保证小的程序单元可判定。可惜的是,Dijkstra并没有证明程序单元,这项工作被科学方法——测试取代了。在保证程序单元可判定的前提下,测试是一种可以对其可证伪的科学方法。命题“天下乌鸦一般黑”就是可以证伪的,我们不可能枚举天下所有的乌鸦,等到哪天找到了一只白乌鸦,我们就可以说这个命题是错误的,这就是证伪。Dijkstra说的“测试只能说明bug存在,而不能证明不存在。”是同样的道理。

测试可以保证,在当前已知情况下,程序单元是正确的。一旦有新的测试用例导致程序单元出错,那么我们就可以修正程序,让程序更加接近真相。这或许就是TDD(测试驱动开发)的妙处所在吧。

去除了 goto 语句之后,我们发现具备顺序,循环和分支判断能力的计算过程还是图灵完备的,也就是说 goto 的有无并不会影响计算能力。那么 goto 的在程序中的作用便是弊大于利的。再加上 goto 的滥用会导致程序结构容易混乱,不利于程序员理解,这更得尽力避免。所以结构化编程限制了对程序直接转移的控制权。

Pointer considered harmful

NbqmyqB.jpg!web

人人都知道面向对象编程有三大特征:封装,继承和多态。

封装是为了构造抽象屏障(Abstract Barrier),到达隐藏信息的目的。任何编程范式都不会缺少封装,因为这是人的需求,是人类简化问题认知的方式。

继承是一种函数(过程或者API)复用的方式,以前我们想在多个结构相似的数据上使用同样的函数,需要通过强制转换到函数可接收的数据类型(结构体指针)上,这必然存在风险。面向对象的世界里,我们不再需要手动强制转换,只要通过显式地表明继承关系,编程语言就能在运行时自动做到这点。

多态(polymorphism)是 一种将不同的特殊行为和单个泛化记号相关联的能力 ,和多态概念对应的参考实现——运行哪段代码的决策叫做分派,大部分分派基于类型,也可以基于方法参数的个数及其类型,而分派的具体执行过程则仰仗函数指针。当作为单个泛化记号的函数被声明出来,它的具体实现可以多样化。通过这样的记号,事实上,我们解耦声明和实现,而这种解耦的过程恰恰是通过函数指针间接地找到目标函数完成的。所以面向对象编程限制了对程序间接转移的控制权。

Mutability considered harmful

FVz67nZ.jpg!web

Neal Ford在《函数式编程思想》( Functional Thinking )中提到面向对象编程是通过封装可变因素控制复杂性(makes code understandable),而函数式编程是通过消除可变因素控制复杂性的。函数式的一个显著的特点就是不可变性。不可变性意味着更多的内存消耗,更差的性能?其实不尽然。像Scala,Clojure这些基于JVM上的函数式编程语言大量使用了持久化结构(如:Persistent Vector,见脚注1),在不损失效率的前提下,实现了不可变的数据结构。这样的数据结构在高并发的环境下具有非常巨大的优势,尤其相对于面向对象编程中为人所诟病的临界区和竞态条件。

不可变的数据结构是无法重复赋值的,所以函数式编程限制了对程序的赋值操作。

小结

鲍勃大叔一针见血地指出,我们过去50年学到的东西主要是—— 什么不应该做 。这等于给全书奠定了基调。可以类比,良好的架构也在传达同样的道理。

为什么从编程范式开始谈起?在审阅完整本书之后,我慢慢发现鲍勃大叔其实在传递一种设计理念:架构设计里,自顶向下的设计往往是不靠谱的。就像本书的目录,从程序的基础构件,谈到组件,最后谈到架构,这个过程非常符合系统自组织的特征。

为什么自顶向下的设计往往不靠谱?本书的第4部分“组件构建原则”会有答案,有需要,且听下回分解。

[1] 函数式编程简介

于 2018-10-21

ymaYrqI.png!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK