读《代码不朽:编写可维护软件的10大要则》C# 版

这本书特别针对没有接受过计算机科学或软件工程专业学习的软件开发人员,这类人员除了熟悉所用语言语法和语义之外,很少接受其他专业培训,对软件工程中的一些概念理解欠缺。软件设计方面考虑较少。如果要成为一个专业的程序员,就需要学习已形成工业化的软件构建方式。

可维护性解释

这本书解释了可维护软件中的“维护”的意思:可维护性是软件质量的一个标准,代表一个系统可被修改的难易程度。所以它是面向程序员的,假设两个软件完成相同的功能,但一个软件的源码,让其他人或者一段时间之后的自己,很难理解,更不用提修改了,就说明这个软件的可维护性比另一个差。软件维护有4种方式:发现并纠正bug(纠正性维护);适应操作系统或运行环境的改变(适应性维护);根据需求增加新的功能(完善性维护);改进代码质量预防bug产生(预防性维护)

三个基本理论

本书提供了10条可以实现高可维护性的指导原则,这些原则背后有三个理论:

1、坚持简单的原则最有助于提高可维护性

2、可维护性不是项目开发完后才去考虑的,而应该是在项目开发的一开始就加以考虑。每个人的贡献都应当计算在内

3、不同原则的违例会带来不同的影响,有些严重程度甚至于他。一个软件系统越遵守原则,可维护性越高。

对10大要则的理解

按照从小到大,从细微到宏观的层次,这本书提取了编写可维护软件中10大编程原则,小到程序开发者应当时刻注意的代码规范,大到系统架构师应该考虑的系统重构、组件和及接口的设计准则。

编写短小的代码单元

代码单元即面向对象编程里的方法或函数。这个原则要求每个函数的长度不应超过15行。

动机

小的函数的好处?作者提出,小的函数容易重用,因为一个巨型的方法会包含很多细节,导致很难有一模一样的场景使用这个方法。作者提出,小的方法更易理解和进行单元测试。若超过15行,则意味着方法可以被拆分了。

如何使用本原则

拆分重构的方式有提取方法和将方法替换为方法对象。

提取方法很容易理解,即从一个函数中提取一段代码,写成一个新的方法。但如果提取方法时发现,这个方法访问了很多局部变量,如果都作为新方法的参数的话,势必会导致参数列表过长。还有返回值的问题,如果这个方法会产生不止一个结果变量。一个重构技巧是将这个方法替换成一个方法对象,将不同的局部变量和结果变量作为类的成员,然后调用类方法。

编写简单的代码单元

这里的“简单”体现代码单元的分支点,所以这个原则可量化为:限制每个代码单元分支点的数量不超过4个。C# 中常见的分支点代码就是if和switch语句。

动机

让代码单元保持简单基于两个原因,一是简单的代码更容易修改,二是简单的代码更容易测试,分支点过多,意味着要有更多的测试用例。

如何使用本原则

复杂的代码单元可能是因为其中包含很多互不相关的代码块,这种情况可以采用“提取方法” 若是其它复杂的情况,比如碰到链式的条件语句,如下判断国旗的语句: ... List result; switch(nationality) { case CHINA: result= new List{Color.RED,Color.YELLOW}; break; case FRENCH: result= new List{Color.BLUE,Color.WHITE,Color.RED}; break; ... }

第一种方法是引入Map数据结构,将国家映射到指定的FLAG对象上; 第二种方法是使用“使用多态来代替条件判断”,实现同一个接口,代表广泛的国旗类型,然后为每个国家的国旗实现一个类。

再比如碰到嵌套的条件语句,为了使代码简单,可以使用“使用卫语句来代替嵌套的条件语句”的重构技巧,即标识出各种独立的情况,并插入return语句来代替嵌套式的条件语句。

例如

if(...) {
  if(...) {
    //TODO CASE1
  } else {
    //TODO CASE2
  }
} else {
  //TODO CASE3
}

可以改写成

if(...) {
  //TODO CASE1
  return;
}
if(...) {
  //TODO CASE2
  return;
}
if(...) {
  //TODO CASE3
  return;
}

可以看到分支点并未减少,然后可以再用“提取方法”减少复杂度。

不写重复代码

对重复代码的定义是,一段至少6行都相同的代码。

动机

如果复制代码,相同的代码出现在不同的地方,不利于源码的定位;如果需要修改的地方正是重复的代码,意味着要做很多重复性的工作,而且容易出错。

如何使用本原则

首先想到的是提取方法;但若是一个方法是另一个类的私有方法怎么办?这时应当将提取的方法放到一个工具类中。 如果重复代码(6行以上完全相同)已不存在,但代码相似,具有相同的逻辑,这时应该考虑提取父类。

保持代码单元的接口简单

限制每个代码单元的参数不能超过4个。

动机

较少的接口参数能够保持简单的上下文,易于重用、理解和修改。

如何使用本原则

将多个参数包装成对象,比如输入坐标参数,x与y,可以包装成一个点对象。 使用“使用方法对象替换方法”的重构技巧,此处和前面有重合。

分离模块之间的关注点

模块对应类的概念。 实际上就是要求类要保持小的体积,不要过大过复杂。

动机

小的体积的类带来了类之间的松耦合,松耦合意味着类能更灵活的适应将来的变化。如果一个类做了很多事情,其耦合度会越来越紧,积攒大量代码,导致代码很难阅读和修改。

如何使用本原则

第一种方法:根据功能将大类拆分为很小的类。一个类一开始可能很小,只是实现单一功能,但都不可避免负责越来越多的职责,当意识到这个类承担了不止一个职责时,就应该将这个类进行拆分。

第二种方法:提取一个接口,实现松耦合。比如一开始为一台相机设计了简单的相机类,只具备拍照,闪光灯打开和关闭3个方法。后来这个类的使用扩展到新的移动设备上,增加了定时功能。这时类变大,而且只有一个类,还需要检查旧设备上的代码有没有受影响。为了降低耦合度,可以使用一个接口,它只定义所有相机都需要实现的功能。

第三种方法:使用第三方库和框架来替代自定义的实现。

架构组件松耦合

组件是比模块(类)更高一层的单元,设计到系统的架构。此原则要求尽可能减少当前模块暴露给(例如,被调用)其它组件中模块的相关代码。

动机

独立的组件可以单独进行维护,方便划分职责,让测试变得容易。

如何使用本原则

使用抽象工厂设计模式,简单的讲就是类的实例不能直接被创建(new一个),而是通过工厂类的方法返回。这种通用的工厂接口背后,隐藏了具体产品的创建过程。在这个环境下,产品通常都不止有一种类型。如果要使用其中的逻辑,需要通过创建通用的工厂对象调用类方法成员。 注:抽象工厂不同于工厂模式,简单理解就是抽象工厂的类型不止一个,所以产品至少有两个。

保持架构组件之间的平衡

保持源代码中的组件数量接近于9。

动机

好的组件平衡让查找和分析代码更容易,提供清晰的功能边界,分离维护职责。

如何使用本原则

软件系统的开发有两种组织模式:

基于功能领域划分的系统:好处是可以从高层功能的角度来分析代码,坏处是技术人员需要了解多个技术栈

基于技术划分的系统:根据技术专长来划分,可能会有前端,后端,接口、日志等组件。

软件架构师需要选择如何组合功能的合适原则。明确系统的领域并坚持下去。

保持小规模代码库

动机

大型系统更加难以维护,易出现更密集的缺陷,以大型代码库为目标的项目更容易失败。

如何使用本原则

功能层面:控制需求蔓延,功能标准化 技术层面:不要复制黏贴代码,重构代码,使用第三方库和框架(这同样是前面提到的准则)

自动化开发部署和测试

测试包含单元测试、集成测试、端对端测试、回归测试、验收测试。不同类型的测试需要不同的自动化框架。

动机

自动化测试可重复,有效率;自动化测试里的断言(assert)可以充当注释;通过编写测试可以反过来推促编写可测试的代码,提高代码质量。 如何使用本原则

使编写单元测试成为每个开发人员的职责,比如使用C#中的单元测试框架Xunit.net。 使用像moq或者mocking这样的技术。stub即测试桩。需要测试桩是因为有些影响测试结果的测试条件是易变、无法统一的。比如拍照,两次拍摄的环境不可能完全相同,结果无法验证,所以需要一个假对象,即测试桩。mocking(模拟)是因为测试中某些函数是沉默的,不包含任何结果,可以在函数中添加计数来验证函数执行过。mock技术有自动化的框架。

建议生产代码和测试代码一比一,提高覆盖率。

编写简洁的代码

给程序开发人员总结了7条“童子军军规”:

1、编写单元级别的良好代码 2、不要编写不好的注释 3、不要注释代码 4、不要保留废弃代码 注:包括3,同时还有其它的形式,比如不可能执行到的代码、无用的私有方法、注释中的代码 5、不要使用过长的标识符名称 6、不要使用魔术常量 注:指表达式中突兀出现的数字,应该先定义。 7、不要使用未正确处理的异常

注:包括以下情况,捕获异常却不处理(catch为空),直接捕获通用异常(比如Exception异常,这些异常不会提供触发失败的状态或事件信息,所以没意义),将异常信息展示给终端用户(避免用户困惑或暴露信息,应该先转换为通用信息)

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java技术

开发中我们需要遵循的几个设计原则!

在软件开发中,前人对软件系统的设计和开发总结了一些原则和模式, 不管用什么语言做开发,都将对我们系统设计和开发提供指导意义。本文主要将总结这些常见的原则和具体阐...

802
来自专栏栗霖积跬步之旅

设计模式的六大原则

单一职责原则: 定义:不要存在多于一个导致类变更的原因。 通俗地说:一个类只负责一项职责。 问题来源:一个类T负责两个职责:职责1和职责2,当因为职责1因需求变...

1840
来自专栏Dawnzhang的开发者手册

开发中我们需要遵循的几个设计原则!(转)

在软件开发中,前人对软件系统的设计和开发总结了一些原则和模式, 不管用什么语言做开发,都将对我们系统设计和开发提供指导意义。本文主要将总结这些常见的原则和具体阐...

452
来自专栏代码世界

6大设计原则总结

6大设计原则总结 一、单一职责原则  单一职责原则:英文名称是Single Responsiblity Principle,简称是SRP。定义:应该有且仅有一个...

2659
来自专栏用户2442861的专栏

浅谈UML的概念和模型之UML九种图

http://blog.csdn.net/jiuqiyuliang/article/details/8552956

541
来自专栏算法修养

PAT 1026

程序运行时间(15) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard ...

34911
来自专栏机器之心

如何写一手漂亮的模型:面向对象编程的设计原则综述

1637
来自专栏微信公众号:Java团长

开发中我们需要遵循的几个设计原则!

在软件开发中,前人对软件系统的设计和开发总结了一些原则和模式, 不管用什么语言做开发,都将对我们系统设计和开发提供指导意义。本文主要将总结这些常见的原则和具体阐...

741
来自专栏大闲人柴毛毛

浅谈代码覆盖

在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况,比如,代码覆盖率必须达到80%或 90%。于是乎,测试人...

3296
来自专栏精讲JAVA

Gof设计模式之七大设计原则(六)

前言 今天的设计原则重理论,举例子只能是文字形式,没法使用代码解释,希望大家可以多读几遍。真正体会到设计原则的好处。每一个设计原则,我都尽量用非...

19610

扫码关注云+社区