引言
近来开始阅读 Robert C. Martin 的新著《架构整洁之道》(Clean Architecture)。没有人愿意看到团队内的项目逐渐发展成一团乱麻,相互纠缠。那如何从根本上减少这种问题的发生呢?这就需要我们掌握一些宏观层面的设计思想,从给项目打基础时就应该考虑好各个模块、类、函数等如何设计成高内聚、低耦合的结构。整洁的项目架构是利于长远发展的,这篇笔记将会记录一些从书中学习到的重要知识点,便于后期复习和参考。
当然,作者还有一本大名鼎鼎的著作《代码整洁之道》也非常值得阅读,关于这本书也做了点笔记,参见《代码整洁之道》读书笔记~
要点记录
三大编程范式是根基,每种范式其实都给我们带走了一些东西:goto
语句、函数指针、赋值。三种编程范式的总结:
- 结构化编程:Discovered by Edsger Wybe Dijkstra in 1968. Structured programming imposes discipline on direct transfer of control.
- 面向对象编程:Discovered by Ole Johan and Kristen Nygaard in 1966. Object-oriented programming imposes discipline on indirect transfer of control.
- 函数式编程:Invented by Alonzo Church in 1936. Functional programming imposes discipline upon assignment.
以上三种编程范式会影响到架构的方方面面:我们使用多态机制跨越架构边界;使用函数式编程影响数据位置和访问;使用结构化编程构建基础算法模块(基石)。可以对照软件架构三大概念:功能、组件隔离和数据管理。
结构化编程
任何程序都可以由三种结构组成:
- Sequence
- Selection
- Iteration
结构化编程允许模块可以被递归地分割成很多小的可被证明的单元,这也就意味着模块可以按照功能解耦
- 科学并非依靠证明某些声明正确而存在,而是证明它们是错误的(Science does not work by proving statements true, but rather by proving statements false)。并非所有的事情都可以被证实的~
- Mathmematics is the discipline of proving provable statements true. Science, in contrast, is the discipline of proving provable statements false
- Dijkstra 曾经说过 Testing shows the presence, not the absence, of bugs。测试并不能保证你的程序一定正确,但是可以通过测试证明你的程序存在错误,并通过充分测试,让你的程序更加正确,直到满足我们的预期
面向对象编程
- 设计良好的架构需要我们理解并应用 OO 的一些原则
什么是面向对象:
- 数据 + 行为的组合
- 为真实世界建模
- 封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)
UNIX 系统要求每个 IO 设备驱动都需要提供五个标准函数:
open
,close
,read
,write
和seek
- 多态实际上就是函数指针的应用,自从冯诺依曼架构诞生以来,程序员们就一直在使用这种技术了。但是显式地使用函数指针是很危险的,并且使用时需要遵循一些原则,比较麻烦。OO 则将你从中解放出来,让你更加轻松地实现多态。
- OO 允许在任何地方,为任何情形使用插件式架构(主要是多态的威力,其实在其它语言中,如 Rust/Go,使用接口机制也能达到类似的目的)
- 使用 OO 语言可以完全控制系统中源码的依赖方向。多态机制可以让任何源码依赖都可以被倒置,无论它们在哪儿
OO 带来的好处,用几个关键字概括:
- 依赖倒置(Dependency inversion)
- 可独立部署性(Independent deployability)
- 可独立开发性(Independent developability)
从软件架构的角度看,面向对象可以通过使用强大的多态机制,来控制系统源码依赖。这样可以创建出插件式架构,可以将更高层的抽象策略和底层的实现隔离开。底层的实现细节交给各个插件去完成,并且它们可以被独立开发和部署,而不会和更高层的模块耦合。
函数式编程
- 所有的竞争条件、死锁条件和并发更新的问题都是由可变变量导致的。函数式编程则限制了变量修改
- 将可变和不可变的组件隔离开来:不可变组件使用函数式执行任务(不使用任何可变量);不可变组件通过和可变组件通信的方式来做状态变更
- Event Sourcing: 核心策略是不存储状态,而是存储事务。当需要获取状态时,只需要从最开始回放事务,最终得到想要的状态。这样的系统就只有 CR(创建、读取) 了,而不会存在状态更新和删除。因此,很多并发引来的更新问题都不会发生(其实可以对比下基于日志式 LSM 的存储引擎)。
总结
结构化编程、函数式编程以及面向对象编程都分别给我们带来了不同的限制(To tell us what not to do),而非带来新的功能。
软件并非快速发展的高科技,因此很多原则至今依然适用。不管机器怎么变化、工具如何变得更好,但软件的本质还是一样。
所谓的软件程序不过就是由顺序、选择、循环结构(间接)组成,不多不少。