缘起
对于一个成熟的程序员而言,软件开发中遇到所有问题,最终都是架构问题。
这个月看了两本书《Clean Architecture》和《The Art of Unix Programing》,为了追求速度看的都是中文版,中间为了保证翻译质量还翻了翻英文原版。《Unix编程艺术》翻译的质量很高,这本书特别不好翻译,看原文很吃力。
软件架构
软件架构的整体目标:降低软件的整体复杂度(Overall Complexity)。
在这样的基础前提下我们看看软件架构整体原则和手段。在看架构的时候,有些书阐述的是原则,有些书更加着重于手段。从根本上讲是同一个东西的不同侧面。
模块化
这个在架构上已经是通识了,也是应对复杂的直接和有力的手段。模块化如果上升到原则层面可以描述为:高内聚和松耦合。通过什么样的指导原则或者手段来划分模块呢?
- 将相同变化速率的代码放到一起
- 将概念上同一个东西的代码放到一起
- 需要放到一起的东西封装在起来,通过接口进行通信
- 设计接口要简洁有力,只给需要的东西,不多给
需要注意的就是模块化是有成本的。抽象的成本是会蠕变,其次通信的成本。在应用模块化的时候需要检测这些成本,过犹不及。
抽象是会蠕变的,有可能让系统剩下的部分变的不那么清晰。这个可以称之为蠕变(creep)或者说是背负(carry on)成本。
依赖管理-让不稳定的依赖于稳定的
这里在架构上也是通识,但在实现中花样有点多。也不是很容易理解。常见的依赖管理手段
- 依赖倒置(DIP)。通过接口在编译期将调用者(caller)依赖于被调用者(callee)的依赖倒过来。让callee依赖于稳定的接口。这里面有个假设就是接口比实现稳定,很多时候这是成立的。需要留意接口常常变化的情况,这时候依赖倒置得不偿失。
- 机制和策略分离。机制是How to do,策略是是What to do。机制受制于基础科学的发展,通常很缓慢。所以相对稳定。而策略是多变的业务规则,通常不稳定。通过机制和策略的分离,提高了机制的复用率。
- 接口和引擎分离。Interface随着受众而变化,但是引擎在针对覆盖的业务部分却相当稳定。一个很好的例子就是
ffmpeg
和其围绕他的一系列GUI工具。 - 通过自动化,生成器来让稳定的元数据(Metadata)可以按需生成的合适数据形式。这也是DRY和SPOT(Single Point Of Truth)的应用。
透明性
这一点在架构上没有得到足够的重视,即使是很小的项目,简单的代码,也要保证其透明性。通常通过下面手段来保证架构的透明性
- 简单的数据结构和算法。
- 通过日志告诉程序做了什么
- 通过telemetry接口,让我们可以检阅当前应用程序的(数据)状态。
- 在分布式系统架构,需要有tracing功能,方便调试业务问题。这在多用户复用相同逻辑的Web应用中特别重要。通过一个唯一ID能Track到整个业务流。
数据为王
在架构中,数据为王。这个被严重低估和忽视了。
- 在简单的数据结构和复杂的算法之间,选择简单的算法。人对数据有很好的感觉,对算法却没有。
- 以数据为中心,数据清晰了,算法就清晰了。
- 业务是数据的流转,程序是个过滤器。
- 数据跟程序一样,有着不同的变化速率和生命周期,有冷有热。注意冷热数据的隔离。
程序性能是个功能选项(Performance is a feature)
- 性能是个feature,不需要就不考虑。需要的话提前考虑。
- 在做决定和调整前先测量,验证自己的
假
设。 - 牛逼的算法在
n
很小的时候很慢,通常n
很小。
Comments: