设计:小即是美

博尔赫斯说:“写散文体的短文——寓言、神话、短故事——给了我某种神秘的满足。想起这些篇章,就仿佛想到硬币:实在、结实、闪光的小物体,更多的东西的样品。”显然,小物体之美,让博尔赫斯着迷。

同样,在软件设计领域里,小的设计同样让我着迷。这里所谓的“小”,并非绝对的小,而是强调一种恰如其分的设计哲学。在开发过程中,每一次迭代的目标不宜设立过大,需小步前行,避免过度设计。在设计开发时,整个系统最好由松散耦合的细小模块组成。这些细小模块由于功能相对独立而单一,因而更易于理解。

在设计系统架构时,我们要注意克制做大做全的贪婪野心,尽力保证系统的小规模。Unix的缔造者之一Dennis Ritchie就曾遭受过将系统做大做全的滑铁卢。他在贝尔实验室的第一个任务,是参与大项目Multics,即开发一个前所未有的、可以多人使用的、同时运行多个程序的操作系统。该项目由贝尔实验室、麻省理工学院和通用电气公司三方联合研制,但是由于设计过于复杂,迟迟拿不出成果,1969年贝尔实验室宣布退出。

△ Unix之父Dennis Ritchie

痛定思痛,Dennis Ritchie和同事Ken Thompson之后在设计Unix时,就吸取了Multics设计复杂而导致失败的教训,提出了"保持简单和直接"(Keep it simple stupid)的原则,即所谓KISS原则。

遵循KISS原则,整个Unix系统由许多小程序组成,每个小程序只能完成一个功能,任何复杂的操作都必须分解成一些基本步骤,由这些小程序逐一完成,再组合起来得到最终结果。表面上看,运行一连串小程序很低效。但是事实证明,由于小程序之间可以像积木一样自由组合,所以非常灵活,能够轻易完成大量意想不到的任务。而且,计算机硬件的升级速度非常快,所以性能也不是一个问题。另一方面,当把大程序分解成单一目的的小程序,开发变得容易,Unix在短短几个月内就问世。

我们千万不要轻视小的力量。

专注的小可以保证独立进化。

从“自治”思想看,它需要实现一定程度的自给自足,并保证对外交互的接口足够稳定。这符合老子的“大国小民”的思想。

这种专注的小首要在于分离职责。一种分离的角度是以“内外之分”观察核心与边缘的职责,然后以模块分离的方式分别为它们寻找到“安身立命之所”。在框架设计上,为保证框架的小,我们常常采用这样的观察视角,这就是所谓的“内核模式”,通过识别出整个框架的核心功能,以此作为整个框架的基础,而其余功能皆可视为框架的外围功能,根据关注的域对其进行切分。这些外围功能互相之间应尽量减少耦合,使其能够独立进化。

△ 内核,即多个集合的共同交集

Spring框架的设计正是遵循了这样的设计理念。除了Spring IoC是整个框架必备的组件之外,Spring MVC、Spring Batch Job、Spring Data等之间没有依赖关系,可以根据项目自身情况酌情裁剪。

那么,如何才能保证设计的系统足够小?

首先,在设计思想上要确立“小即是美”的美学观,要清晰地辨别且能够欣赏小的灵活之美完整之美轻盈之美。只有在思想上认同它,你才能顺势而为;只有从心理上感受到这种美丽,你才能响应它的召唤。

灵活之美,在于它能快速地响应变化,这种变化可能是局部的,也可以是整个设计方向的改变。例如,在多数企业系统和互联网系统中,都需要分离Online和Offline任务,以指定不同的架构决策。又例如,我们可以设计独立的、具有最小功能子集的Batch Job来承担后台任务。这些Batch Job可以作为一个单独的应用程序执行在单独的进程中。一旦需求要求我们对设计做出改变,我们也能将修改控制在足够小的范围中,从而保证对整个系统不会带来巨大的影响。

若遵循EDA(Event Driven Architecture)模式,我们可以根据业务领域的不同,设计出功能最小完整的自治组件。组件之间的通信通过事件来传播,利用发布者/订阅者的方式解除组件之间的耦合;又或者利用消息传递来处理业务逻辑,例如在AKKA中,我们可以设计出灵活而小的Actor对象;而微服务(Micro Service)架构则从服务级别展现了设计的灵活之美。

轻盈之美,体现在它的功能并不臃肿,对外部的依赖较少,既容易在系统中快速引入,又不会使原有系统变得笨重,还能很方便地部署或者启动。

展现了轻盈之美的组件往往具有良好的可测试性。我们可以利用六边形架构将系统分隔为内、外两个边界,凡是系统对外的通信,皆通过端口(Port)和适配器(Adapter)完成,这样就能较好地解除对外部环境的依赖,提高系统的可测试性。而清晰的边界划分也是设计小组件的一种有效手段。

△ 六边形架构,又称为Port-Adapter模式

若对于框架或平台而言,则需要尽力降低框架或平台的侵入性。当年Rod Jonson之所以提出J2EE Without EJB,正是因为EJB的侵入性带来了诸多病症。

当然,从另一个角度来讲,我们自己研发的产品或项目也要尽可能摆脱对外部资源的依赖,即所谓“稳定依赖原则”。Robert Martin提出的Clean Architecture清晰地勾勒出这样的思想。在Clean Architecture的表述中,他让外部易变的部分依赖于更加稳定的部分,如域模型,而非形成相反的依赖关系。这样还可使实现变得更易于变化;多变的部分依赖于稳定的部分。好架构就要能轻松地改变那些易变的决定。

△ Clean Architecture

完整之美,在于它是自足的。完整并不意味着大而全,而在于它足够精简,没有冗余。当然,它同时应该是没有残缺的。残缺,意味着它无法在没有外部支持的情况下,完成自己应该完成的工作。这种美感符合“麻雀虽小,五脏俱全”的标准。Standalone的微服务,正好体现了这种自容器的完整之美。

小的益处还有一点,它可以使得我们在架构决策或技术选型时,可以变得更加从容。

譬如说,因为某些原因我们需要将整个企业系统从WebLogic上迁移到JBoss上,无疑,这是一个艰难的决定,实施起来更是一个漫长的过程。如果系统是基于Micro Service的架构风格进行构建,每个服务根据各自情形选择自己的技术栈。倘若需要对某些服务进行技术栈迁移,相信这个问题不再变得棘手。——大象可以轻盈地跳舞,但付出的努力会百倍于一只敏捷的狐狸。

如今,Java已经发展到Java 8,引入的Lambda表达式等多个特性如此鲜嫩,让人垂涎不已。然而据我所知,国内多数企业的Java项目仍然停滞在JDK 6裹足不前。是JDK 8不够好吗?非也。盖因为求稳的他们仍然心存顾虑。即使Oracle号称这种JDK的迁移多么的平滑,多么的稳健,多数企业仍然不敢轻易做出迁移的决定。若因为迁移而带来未知的缺陷,可谓得不偿失。既然现在项目运行良好,何必冒此风险。

于是,我们这个行业因为系统的庞大而变得守旧老成,亦步亦趋。并非大家没有冒险的精神,实则是庞大的项目难以灵活地改变方向。倘若只是更新系统中的某一个库或框架,形势就截然不同了。记得在没有lambda的时代,当我们让客户看到了Guava的好处时,要引入Guava就轻而易举,真如顺水推舟了。

当我们发现某些功能具备独立和专注的特征时,都是可能做出小系统的机会。这些小系统并不一定是子系统或模块,它还可以是一个独立的应用或服务。

例如在一个税务系统中,需要生成复杂的税务报表。它的整个逻辑是相对独立的,不管是报表的动态生成,格式的转换,数据的查询以及流的处理和PDF文档的生成,都与系统其他部分关联不大。唯一可能与系统存在紧密关联的是数据库。但为了解决高峰期的性能问题,我们可以建立单独的数据提取器,又或引入流处理,定期将数据提取出来,放入内存数据库中。将这样相对独立的功能做成服务,就能够独立演化,并有效支持服务请求的可伸缩。这样的小型服务可以更灵活地应对变化。当我们发现内存数据库不能满足大量请求时,也可以轻而易举地将其迁移到NoSQL上,并根据数据的属性例如按照地域进行分区,支持水平扩展。

若要保证系统的小,我们还可以尝试使用脚本。在开发软件系统时,可以使用一些脚本语言来开发一些小工具,以应对灵活的需求变化,消除重复代码,实现某些步骤的自动化。例如用Groovy编写一些函数,用Ruby编写代码生成工具,又或者使用Gradle、SBT实现系统的自动部署,启动服务器等脚本。脚本语言具有很好的灵活性,而动态语言的特性也使得我们能够编写出短小精悍的超级小工具,甚至可以作为系统模块之间的粘合剂,如机器齿轮上的润滑油一般,让整个系统充满活力。

Russ Miles认为:团队的开发速度经常因编写代码体积和复杂度的增长而放缓。他认为组件结构在简化架构方面非常重要,并提出了Life-Preserver模型。这是一个环形结构,所有的基础设施软件都在环上处理集成,而核心业务组件在环内加入业务价值。这种模型的核心是利用事件来简化架构。

使用事件的一个显著特点是解耦,使得事件的发布者与订阅者都可以独立演化,也可以自由增加事件的订阅者数量。我们可以将事件的发布者与订阅者设计为独立的小程序,就像Unix系统中那些小工具一样,通过管道建立关联。这样的设计思想称之为EDA(Event Driven Architecture)。

当然,要做到系统的“小”,必然也是要付出代价的。奥卡姆剃刀定律认为:“若无必要,勿增实体”。剖析Kent Beck提出的“简单设计”原则,在满足了客户功能、无多余重复、清晰表达设计意图的前提下,需要遵循奥卡姆剃刀定律。盖因为系统分解得约小,就会因为“实体”数量的增加,引入额外的复杂度,包括对实体的管理、实体之间的协作等。显然,任何设计决策都有其两面性,我们需要放入到当下的上下文(Context)中做出正确的判断。正如爱因斯坦所说:“让它尽可能简单,但不要过于简单。”看来,我们对任何事情都需要把握一个“度”,水满则溢,月盈则亏,故而需要损有余而补不足。

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2016-07-04

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器人网

解读工业机器人的大脑:控制系统基础

如果工业机器人只有主体和驱动器,机械臂是不能正常工作。原因是传感器输出的信号没有起作用,驱动电动机也得不到驱动电压和电流,所以机器人需要有一个控制器,用硬件坨和...

3266
来自专栏诸葛青云的专栏

C语言精通才是真的黑客!

黑客是一个中文词语,在台湾地区对应的中文词语为骇客,皆源自英文hacker,不同地区的中文使用习惯造成了翻译的差别。实际上,黑客(或骇客)与英文原文Hacker...

943
来自专栏企鹅号快讯

CRM重构之——微服务设计导读(一)

在介入正题前,想谈一下如何阅读,因为技术类的文章虽好,但需要一定的门槛,而且会比较枯燥,读后可能很快就会忘记读了什么,只记得读过。 导读 阅 带着兴趣 带着兴趣...

2389
来自专栏二进制文集

编程大师访谈录

692
来自专栏C/C++基础

浅论C++的复杂性

C++语言已经有了30多年的历史。作为一门影响广泛的编程语言,它所受到的关注和争论恐怕是任何一门其他的语言所不能比拟的。十几年前,Java等新生语言的出现曾导致...

552
来自专栏技术分享

微服务架构—自动化测试全链路设计

从 SOA 架构到现在大行其道的微服务架构,系统越拆越小,整体架构的复杂度也是直线上升,我们一直老生常谈的微服务架构下的技术难点及解决方案也日渐成熟(包括典型的...

1080
来自专栏软件测试经验与教训

网上看到的面试题,我忍不住吐槽....

3388
来自专栏腾讯Bugly的专栏

2016 移动应用质量大数据报告

2016年,在“互联网+”战略的推动下,移动互联网与越来越多传统行业的结合更加紧密,用户使用移动互联网的工作场景、生活场景、消费场景都在悄然发生着改变, 移动互...

4025
来自专栏PPV课数据科学社区

Python共享单车数据分析

这是法国的一家共享单车数据,共7条路线。 首先我们预览下数据 ? what is this? 这怎么分析?别怕,有Pandas ? 这就是Pandas的威武...

2726
来自专栏灯塔大数据

原创译文 | Sphero发布Spark Bolt,AI机器人进攻教育领域

本文为灯塔大数据原创内容,欢迎个人转载至朋友圈,其他机构转载请在文章开头标注:“转自:灯塔大数据;微信:DTbigdata”

1093

扫码关注云+社区