读源码学架构系列:面向对象设计的原则

这一篇开始会探讨一下面向对象的设计原则,这些原则是大师们多年经验的总结,这些总结乍看上去非常简单,但是如果你的功力不够你是无法理解这背后想表达的思想,如果你能理解,那可能也是经历过痛的领悟吧。对这些大师们经验成果的总结,首先需要保证正确的理解,希望我的理解没有偏离得太远。

本文大纲:

  • 写在前面的废话
  • 敏捷实践原则
  • 软件设计拙劣的表现
  • 面向对象设计的原则
  • 关于软件设计

0x01 写在前面的废话

其实在最初计划写这个系列的时候,关于面向对象设计的原则就有考虑在内,最初时,自己对这几篇的内容也没有什么信心,因为我觉得自己对这些原则的理解就不够深,并且,对于理论性的知识,每个人的理解和思考都会不一样。尽管如此,我还是想挑战一下自己,计划针对这几大原则各输出一篇文章,目的是希望在输出的过程中,能够让自己对这些经典的设计原则有更加深刻的理解。

SPI的几篇写完之后,为了准备这几篇,我又重新去找Uncle Bob关于这几大原则的相关视频,但是挺遗憾的是,所有找到的视频都只有一部分,并没有涉及到全部的五大原则。

同时,我想起了书柜里十年前购买的那本Uncle Bob的大作《敏捷软件开发:原则、模式与实践》,于是便拿起这本书重读。当年之所以会购买这本书是因为入行之后头几年,几乎每天都会泡在javaeye的论坛中,论坛中的大部分栏目下的文章列表都被我从头至尾的读过,包括所有参与的讨论,那几年的javaeye技术氛围特别好,里面聚焦了一批业内的高手,讨论了很多前言的话题,也是在那里我头一次看到有人用C通过自己的封装之后,尝试以OO的方式用C语言编程,直到后面两三年后,我在学习Golang的时候,在云风的博客中看到云风大佬在尝试了Golang之后写的一篇文章,我才知道,当年在javaeye那个用C语言尝试面向对象编程的就是他。

扯远了,回到Uncle Bob的这本书。当年买回来后,我断断续续的看过几回,因为自己的功力不够,每次拿起没多久就放下了,后面的一些年里也偶尔拿起来过,但也没有认真的看过。严格的说,也不算是没有认真看,而是当年的我,入行还浅,当面对各种新鲜的名词:敏捷、XPScrum、结对编程、测试驱动开发、充血模型、持续集成等等的时候,自己的注意力全在这些术上了,因为自己的功力太浅,没办法拨开云雾去关注这些名词背后的道。

这交拿起这本书的时候,读着读着,感觉自己有了一些不一样的感受。以前看到敏捷实践的那些原则,根本没办法明白到底是在说什么,现在看到这些原则,读到每一条的时候,脑海里似乎都能闪现出一些这些年所经历过的一些反面教材的画面,似乎现在明白了为什么要总结出这些原则。

同时,对于每一条面向对象设计的原则,以前读过不知道多少次的东西,这次阅读时似乎感受不太一样了。

经典的书每次读都会有不同的感受,接下来的几篇权当记录一下到今天为止我个人对面向对象的五大设计原则的浅薄理解吧。

0x02 敏捷实践原则

敏捷开发倡导以微小增量的方式构建软件,在每次迭代中,团队改进系统设计,使设计尽可能适合于当前系统,并使它尽可能地好。

敏捷实践的12条原则:

  1. 我们最优先要做的是通过尽早的、持续的交付有价值的软件来使客户满意。
  2. 即使到了开发的后期,也欢迎改变需求。敏捷过程利用变化来为客户创造竞争优势。
  3. 经常性地交付可以工作的软件,交付的间隔可以从几周到几个月,交付的时间间隔越短越好。
  4. 在整个项目开发期间,业务人员和开发人员必须天天都在一起工作。
  5. 围绕被激励起来的个人来构建项目。给他们提供所需要的环境和支持,并且信任他们能够完成工作。
  6. 在团队内部,最具有效果并且富有效率的传递信息的方法,就是面对面的交谈。
  7. 工作的软件是首要的进度度量标准。
  8. 敏捷过程提供可持续的开发速度。责任人、开发者和用户应该能够保持一个长期的、恒定的开发速度。
  9. 不断地关注优秀的技能和好的设计会增强敏捷能力。
  10. 简单--使未完成的工作最大化的艺术--是根本的。
  11. 最好的架构、需求和设计出自于自组织的团队。
  12. 每隔一定时间,团队会在如何才能更有效地工作方面进行反省,然后相应地对自己的行为进行调整。

这里提到自组织的团队,那什么是自组织的团队?

敏捷团队就是自组织的团队,任务不是从外部分配给单个团队成员,而是分配给整个团队,然后再由团队来确定完成任务的最好方法。团队的成员共同来解决项目中所有方面的问题。每一个成员都具有项目中所有方面的参与权力。不存在单一的团队成员对系统架构、需求或者测试负责的情况。整个团队共同承担那些责任,每一个团队成员都能够影响它们。

我个人觉得这些原则从一个简洁但比较全面的角度去描述了如何去做好一个项目的项目管理工作。我从这些原则里读到了:项目目标、需求变更、持续交付、持续验收、团队协作、面对面沟通、快速迭代、简单设计、过程改进等等。按照这些原则可以让我们的开发工作呈螺旋式上升,避免与正确的目标渐行渐远。

0x03 软件设计拙劣的表现

软件腐化(设计拙劣)的表现:

  • 僵化性(Rigidity):很难对系统进行改动,因为每个改动都会迫使许多对系统其他部分的其他改动;
  • 脆弱性(Fragility):对系统的改动会导致系统中和改动的地方在概念上无关的许多地方出现问题;
  • 牢固性(Immobility):很难解开系统的纠结,使之成为一些可在其他系统中重用的组件;
  • 粘滞性(Viscosity):做正确的事情比做错误的事情要困难;
  • 不必要的复杂性(Needless Complexity):设计中包含有不具任何直接好处的基础结构;
  • 不必要的重复(Needless Repetition):设计中包含有重复的结构,而该重复的结构本可以使用单一的抽象进行统一;
  • 晦涩性(Opacity):很难阅读、理解,没有很好地表现出意图;

这些翻译过来的名词有些晦涩难懂,但可以通过后面的解释来理解它们所表达的意思。它们主要是描述一些不好的软件设计的表现,这些表现意味着设计出的软件推动了灵活性、可维护性以及可重用性。

0x04 面向对象设计的原则

面向对象设计的原则如下:

  • 单一职责原则(SRP
  • 开放-封闭原则(OCP
  • 里氏替换原则(LSP
  • 依赖倒置原则(DIP
  • 接口隔离原则(ISP

这些原则是软件工程数十年的经验成果。面向对象设计的原则有助于开发人员去诊断设计中的问题,并为当前的需求构建出最好的设计。

但是,同时也要明白,仅仅因为是一个原则就无条件的去遵循它的做法也是错误的,过分遵循这些原则会导致不必要的复杂性。

0x05 关于软件设计

什么是软件设计?

通常,我们可能以为设计就是一级和代码分离的UML图。一组UML图也许描绘了设计的一部分,但是它不是设计。软件项目的设计是一个抽象的概念,它和程序的概貌、结构以及每一个模块、类和方法的详细结构有关。我们可以使用许多不同的媒介(如UML、文档等)去描绘它,但是它最终体现为源代码。即:源代码就是设计

敏捷团队依靠变化来获取活力,团队几乎不进行预先设计,因此,不需要一个成熟的初始设计。他们更愿意保持系统设计尽可能的干净、简单,并使用许多单元测试和验收测试作为支援。这保持了设计的灵活性、易于理解性。团队利用这种灵活性,持续地改进设计,以便于每次迭代结束所生成的系统都具有最适合于那次迭代中需求的设计

敏捷开发人员致力于保持设计尽可能地适当、干净。这不是一个随便的或者暂时性的承诺。敏捷开发人员不是每几周才清洁他们的设计。而是每天、每小时、甚至每分钟都要保持软件尽可能地干净、简单并富有表现力。

设计必须要保持干净、简单,并且由于源代码是设计最重要的表示,所以它同样要保持干净。作为软件开发人员,不能忍受代码腐化。

敏捷设计是一个过程,不是一个事件。它是一个持续的应用原则、模式以及实践来改进软件的结构和可读性的过程。它致力于保持系统设计在任何时间都尽可能的简单、干净以及富有表现力

作为开发人员,我们应当:

  1. 遵循敏捷实践去发现问题;
  2. 应用设计原则去诊断问题;
  3. 应用适当的设计模式去解决问题;

软件开发的这三个方面间的相互作用就是设计。

0x06 总结

对我个人而言,上面的三点要求让我有种菩提灌顶的感觉。

回顾这些年下来,对于敏捷实践的原则一直尽量在项目开发的过程中去做到这些,但内心一直有一种有形无神的感觉。包括这些年经历过的一些号称敏捷开发的团队也是一样,多数情况下都是使用了一些敏捷开发的工具或手段,本质上都有形无实。反思可能的原因:没有思考过为什么要使用敏捷开发实践。比如,为什么要持续交付且尽可能早的交付?为什么要迭代开发?为什么要过程改进?为什么要单元测试和验收测试?它们各解决什么问题?因为践行这些敏捷实践的原则可以帮助我们快速发现问题。

再看面向对象设计的原则,这五条原则这些年可以说不知道看过多少次,偶尔回看的时候会有些许的感悟,但一直只是肤浅的去从表面去理解这些设计原则,上面要求的第二点明确的告诉我们,这些设计原则是用来诊断我们设计中的问题的,真是一语惊醒梦中人。明白了它们的用途,再回头看这些原则,比如单一职责原则,反问自己真的理解单一职责原则吗?什么是职责?如何理解这里的单一?在设计时如何去平衡避免过度设计?

还有设计模式,最早的《Java与模式》那本比砖头还厚的书(我从头至尾看过一遍)以及后面其他一些关于设计模式的书,书看过不少,代码也写过不少,从最早的为了模式而使用设计模式,到后面的心中无模式,真正把设计模式用好的时候不多。反思原因:每种不同的设计模式都是用来解决一些特定的问题的,要解决问题首先就要识别出问题,那如何正确的识别出问题呢?这就要用到前面的设计原则了。如果都不能正确的识别出问题,那能用好设计模式吗?

之所以说前面的三点要求让我感觉茅塞顿开,对我而言,过去都是从单个的角度去看去理解敏捷开发、设计原则和设计模式,没有从根本上真正的理解它们的用途,更没有思考过这三者之间的关系。

接下来会针对五大设计原则逐条的分析理解。

期待各种交流与反馈!

Reference:
  1. 敏捷软件开发
  2. 云风:我所偏爱的C语言面向对象编程范式