简介
就像修炼武学一般,编码的技能同样需要修行,只有掌握更多编码技能与设计技能的程序员才能在程序世界走得更远,攀得更高。掌握必备的设计技能,就好像是武者修行的洗髓炼气,决定了未来内力的强大;扎实的编码功底,则是修炼外功,打磨的是筋骨皮;至于开发工具、测试驱动、重构等诸多技能,则是编码武者掌握的招式,若能熟练掌握,就能一击制敌,让那些糟糕代码无容身之地。故而,程序员能力的提升,就是编码武者的修行。
目标
了解编码驱动设计高内聚松耦合的本源特点
系统学习编码技能与设计技能的知识原理
受众人群
希望提升编码与设计技能的程序员
软件架构师、开发工程师
课程时长
2天(12H)
分享提纲
主题 | 内容 | |
一、洗髓篇 | ⾼内聚松耦合 | * 内聚性:即软件单位内部的关联紧密程度,这意味着在软件设计中,我们需要合理分辨对象的职责,并让对象尽可能满⾜单⼀职责原则。⽽所谓“职责”,就是变化原因,因⽽保持内聚性,就是要充分地识别变化。 * 耦合性:指两个或多个软件单位之间的关联紧密程度。如果软件单位之间存在耦合,就说明它会因为变化⽽产 ⽣影响,属于需要封装变化,保证API的抽象。 因⽽,⾼内聚松耦合原则推导出⼀个重要概念,即“变化”。⽽要有效地应对变化,对内则需要⾼内聚,对外则需要保证松耦合。 Ø 案例分析:邮件转发系统的设计问题 |
设计的起点 | 无论遵循什么样的设计原则和设计方法,其目的都是要改进设计质量。然而,首先需要一个好的设计起点。 如果运用面向对象设计思想,设计的起点就是寻找对象;而对于函数式编程思想,设计的起点则是寻找函数。 对象(自治对象)的特征:最小完备;自我履行;稳定空间;独立演化。 函数(纯函数)的特征:原子抽象;不变性;可组合。 寻找对象的方法包括:名词动词法;CRC;领域驱动设计;测试驱动设计;职责驱动设计;ICONIX;用例驱动设计。 Ø 案例分析:薪资管理系统 | |
重复谜题 | 高内聚松耦合的目标是应对变化,而解决变化的首要条件是处理重复。一旦系统中存在重复的知识(业务逻辑、解决方案),当知识发生变化时,与之相关的内容就会受到影响。 l 业务逻辑的重复 针对业务逻辑的重复,解决的原则就是关注点分离。从OO思想看,设计在满足单一职责原则的同时,还要遵循由Mayer提出的CQS原则;而结合FP思想,则是尽可能地设计纯函数,将副作用无限制地往外推。 针对函数级别的设计,应追求层次化的分解,遵循单一抽象层次原则,这样的代码就能做到不言自明,若有好的命名,读之若自然语言,并且更加精简准确。 若设计为纯函数,则可以利用组合子思想,对函数进行原子抽象,再通过组合的方式满足具体的业务逻辑,能更加充分地满足重用。 Ø 案例分析:日志系统的组合子设计 类级别的重用仍然遵循关注点分离原则,不同之处在于分离的方向。若分离方向为“向上”,则采用共性与可变性分析与差异式编程。若分离方向为“向外”,则满足扩展式设计,将两个(或多个)不同变化方向的职责分离。 Ø 案例分析:业务系统的数据库访问与事务处理,采用两种不同方式对其进行重构,重构的结果是一个初步简略的框架。 l 程序结构的重复 在结构上完全相同,差异在于内部细节,且无法运用OO的参数或多态形式消除变化带来的影响。 Ø 案例分析:对集合的操作,运用函数式编程思想消除重复。 | |
对象的合理封装 | 一、分辨职责,即弄清楚职责应该分配给什么对象。这个识别职责的过程同时也是寻找对象的过程。在这个步骤中,可以尝试用一句话来描述职责,只要你描述清楚了,则它应该依附的对象就应该自然而然显现。 二、判别哪些是实现细节,那些是可以公开的接口,以保证对细节的合理隐藏。暴露太多的细节可能会产生不合理的依赖,而从职责的角度来讲,这些细节并不是调用者所关心的内容。 对象的封装应该遵循信息专家模式与迪米特法则,又或者从代码层面,识别出的坏味道为Feature Envy。 Ø 案例分析:报表系统之参数处理 | |
⾃治对象 | 对象的智能化是由对象拥有的信息所决定的。因为就该职责而言,他拥有这些信息,就等于拥有了更多处理信息的知识,因此,只能由他才有足够的“聪明才智”,知晓如何处理这些信息,合理地响应调用者发出的请求。 自治对象的特征包括:最小完备,稳定空间,自我履行与独立进化。 Ø 案例分析:设备状态的变迁 | |
易筋篇 | 整洁之道 | 武功修行,内外兼修才是王道。软件设计同样如此,不能只炼心法(设计),而缺少对身体(代码)的锤炼。二者(设计与代码)并非完全割裂的关系,而是相辅相成,甚至内外相通的关系。好的设计可以在一定程度上保证好的编码,而把握好整洁代码的特征,培养编码的Sense,则有助于改进设计的质量。 那么什么才是整洁代码呢?本章会对此展开探讨,以期端正编码者的态度,培养良好编码习惯,打磨编码技能。 |
负重修行 | 外功修炼就是要负重而行。在这条修行道路上,我们需要突破如下内容的桎梏: 名:提高可读性的一方面; 形:提高可读性的一方面; 函数:构成程序的最重要元素。 可读性 命名 可读性的一个关键是命名,要求对变量、函数、类以及模块等的命名能够很好地传达设计者意图,让代码尽可能地清晰易懂。命名时,应充分考虑对各种词性的运用,避免过长的名称,避免因为命名不当导致程序出现歧义。 表达式 不要将表达式的细节暴露在外,这会认为制造阅读代码的障碍,针对长表达式,更需要对表达式进行合理封装。有时候,我们在使用条件判断时,可能用错了地方,没有将真正存在差异的地方分辨出来。我们需要让判断条件做真正的选择。要注意处理布尔型标志参数。 合理的分段 在编写代码时,通过对代码进行合理的分段,可以使得代码结构更加清晰,可读性更高。分段时,也是对代码的理解,尤其是促进对职责的分辨。此时,可以考虑标注合适的注释,使得代码更容易懂。同时,这种分段与注释,其实也是对代码进行改造的基础。 | |
DSL | DSL(Domain Specific Language),即领域特定语言。它通过运用一些模式与方法,使得代码更好地体现领域知识,展现出让领域专家也能读懂的良好可读性。 在编写代码时,我们最常使用的一个公共 DSL 模式,称之为“连贯接口(Fluence Interface)”,他将其定义为能够为一系列方法调用中转或维护指令上下文的行为。 Ø 案例分析:若干代码片段,多数来自于真实项目的丑陋代码,演示如何提高这些代码的可读性,使得代码结构更加清晰。 | |
整洁的函数 | 函数的第一规则是要短小。第二条规则是还要更短小。它应该遵循单一职责原则。保证函数只做一件事,就可以有效地保证写出短小的函数,写出可读性高的函数;同时也有利于函数的重用。前面的案例正是因为违背了这一原则,导致函数变长,且不易理解。 阅读函数时,要注意识别和提取无关的子问题域,辨别函数的最高目标,从而改进函数。应保证每个函数一个抽象层级。 Ø 案例分析:Fitness代码分析 | |
异常处理 | 使用异常替代返回错误码。 采用错误码,就不可避免需要使用分支,从而可能导致更深层次的嵌套结构。其次,若能使用异常来处理错误分支,则可以使得错误处理代码能够从主路径代码中分离出来。 Ø 案例分析:版本升级管理系统的异常处理 | |
重构 | 重构意即:在不修改原有功能的情况下改善代码。因此,在开发时,存在两项工作:添加功能和重构。这两顶帽子应该频繁交换,但在同一时间只能戴一顶帽子。 识别代码的坏味道,包括:重复的代码、过长的方法、过大的类、发散式变化、霰弹式修改等。 使用IntelliJ IDEA的重构工具,对代码进行自动化重构。 Ø 案例实践:影片租赁系统 | |
处理遗留代码 | 通常情况下,我们不需要去修改已经提供了正确功能的软件。只有基于如下四个原因,我们才需要修改软件: 添加新特性;修正缺陷;改善设计;优化资源使用。 | |
单元测试 | 明确单元测试的定义:运行快,不依赖于任何外部资源的测试就是单元测试。 案例分析:大型通信平台中关于报文处理的测试数据准备 在遇到以下情况下,我们需要运用Mock技术,即当被测试类所依赖的第三方类: * 运⾏时间过⻓时 * 访问了外部资源时 * 返回结果不确定时 | |
如何解除依赖 | 遗留代码的最大问题是各种类的职责纠缠在一起,牵一发而动全身。既不利于维护,更难以增加新功能。若要对代码进行重构或修改,安全的做法是为其添加测试,要利于测试,就需要解除依赖;而改进代码的质量,同样需要解除依赖。 解除依赖的办法包括: * 参数适配; * 接口提取; * 引入实例委托; * 解除具体耦合。
Ø 案例分析:订单管理系统的解耦设计 | |
阅读和理解代码 | 要更好地改进遗留代码,那么一个首要前提就是要理解这些遗留代码。由于遗留代码意味着它已经存在,而且该系统是可以运行的,所以可以从该系统的使用者角度去思考它所拥有的功能。 采用的方法包括: * 绘制代码的包图; * 绘制影像图; * 绘制特征草图。
Ø 案例分析:报表系统的模块设计 |
Bruce Zhang
百林哲咨询(北京)有限公司专家团队成员
Bruce Zhang
百林哲咨询(北京)有限公司专家团队成员
Bruce Zhang
百林哲咨询(北京)有限公司专家团队成员
Bruce Zhang
百林哲咨询(北京)有限公司专家团队成员
Bruce Zhang
百林哲咨询(北京)有限公司专家团队成员
Bruce Zhang
百林哲咨询(北京)有限公司专家团队成员
Bruce Zhang
百林哲咨询(北京)有限公司专家团队成员