由学习《软件设计重构》所想到的代码review(一)

Paste_Image.png

前言

对于一个程序员来讲如何来最直接的来衡量他的技术能力和产出呢?我想最直观的作法是看他的代码编写能力,就拿我经常接触的一些程序员来看,他们买了很多技术重构类书籍,但是看完后代码编写能力并没有显著提高。有人说可以用代码review工具啊,但是像市面上的这些代码review工具,只能帮助我们解决表面的bug和规范点,还无法帮助我们发现更深层次的设计问题。

下面我将结合《软件设计重构》这本书谈谈在进行代码review的时候,需要关注的哪些点。

一、技术债务

何为技术债务?

技术债务是有意或无意的做出错误的或非最优的设计决策所引发的俩务

我们在代码review的时候,经常碰到一些实现有瑕疵的方案,然后对方说因为时间太紧急临时采用的方案,等第二期项目将其完善,于是往往第二期以后这个临时方案就很难再去触动了,时间越长代码冗余越大,越难去做修改,于是这就是典型的技术债务,债务越积越多,最后只能重新彻底重构项目才能解决问题,这也叫做技术破产。

于是我们的做法有一个债务管理系统,在代码review的时候,会将这些债务或者临时方案录入到系统中并制订偿还日期,那么后续债务顺利偿还后,更改系统状态,否遭遇一直没有偿还的,系统将以邮件的方式提醒,债务累积到一种数目后将与绩效挂钩考核。

二、设计的坏味道

前面只是从债务的角度说明了所带来的危害,其实引起技术债务的一个很重要的原因是对设计坏味和重构认识不足。

我们从设计的角度来看代码时,要遵循六要素:

  • 可理解性
    代码理解起来的难易程度
  • 可修改性
    在修改既有功能时,不会导致连锁反应。
  • 可扩展性
    支持新功能,不会导致连锁反应
  • 可重用性
    可以在代码的其他地方引用其一块代码
  • 可测试性
    项目要能够支持单元测试
  • 可靠性
    在正确的实现了功能的同时,也能够考虑各种异常情况如何容错

2.1、设计坏味的分类

Paste_Image.png

2.1.1 抽象型坏味道

Paste_Image.png

1、缺失抽象
举例说明:

  • 问题点:
    在JDK1.0中方法printStackTrace()以字符串的方式将栈跟踪打印到标准错误流:
public class Throwabe {
    public void printStackTrace();
}

在需要以编程方式访问栈跟踪元素的客户程序中,必须要编程代码来获取数据,如行号等,由于客户程度依赖这种字符串格式,JDK设计人员只能在后续版本中兼容这种格式了。

  • 解决方法
public class Throwabe {
    public void printStackTrace();
    public StackTraceElement[] getStackTrace();
}

从Jdk1.4起对JAVA的API进行了改进,StackTraceElement类就是原来设计中缺失的对象,定义如下:

public final class StackTraceElement {
    public String getFilename();
    public int getLineNumber();
    public String getClassname();
    ......
}

2、命令式抽象
举例如下:

  • 问题点:


    Paste_Image.png

    注:其中每个类都只包括一个方法,这些方法分别是:create、display和copy等,因此存在命令式投象坏味,这种问题不仅会增加类的数量,还会增加开发和维护工作复杂性,而且将本应内聚的方法进行了不必要的分享。

  • 解决方案


    Paste_Image.png

    注:根据高内聚原则,统一归集到一个Report类中。

3、不完整的抽象
抽象未支持所有互补或相关的方法时,将导致不完整的抽象,比如一个抽象的公有接口提供了用于分配资源的initalize()方法,但是却没有提供删除或者回收资源的方法dispose(),这种情况下就属于不完整的抽象。

一些常见的互补方法对如下:

列一 列二 列三 列四
min/max open/close create/destroy get/set
start/stop print/scan first/last begin/end
source/target lock/unlock show/hide up/down
enable/disable acquire/release left/right on/off

4、多方面的抽象
对象被赋予不止一项职责时,将导致这种问题。

举例如下:

  • 问题点
    java.util.Calendar类承担了多项职责,不仅提供了日期相关的功能,还提供了与时间有关的功能,存大多方面抽象。由于同时支持日期和时间的方法,Calendar类接口很大且难为理解,在JDK7中,java.util.Calendar类包括了2825行代码,有67个方法和71个字段。

  • 解决方案
    对于Calendar类,一种可能的重构是,将Calendar类与时间相关的功能提取到新类Time中,并将相关方法和字段移到新提取的类中,在Java8中引入了一些支持日期和时间的新类,这些类位于java.time中。

5、不必要的抽象
举例如下:

  • 问题点:
public interface WindowConstants {
    public static final int DO_NOTHING_ON_CLOSE=0;
    public static final int HIDE_ON_CLOSE=1;
}

注:这个接口是典型的常量接口javax.swing.WindowConstants,为啥用接口来存储常量,因为首先枚举是jdk1.5才引入的,其次通过接口中定义常量,可方便类通过继承而不是委托来使用它们,因为通过实现接口,类可方便的访问接口中的常量,为什么不使用类来存储常量呢,因为接口支持多继承。
那么接口这样定义常量有哪些问题呢?
A、派生类被无关的常量影响。
B、这些常量属于实现细节,通过接口暴露它们违反封装原则。
C、接口中存储常量,修改它们会影响其他使用者。

  • 解决方案
    将WindowsConstants定义为枚举,直接使用。

6、重复的抽象
根据DRY原则规定:对于每个技术点,系统中都只能有一个明确的表示。
导致重复抽象的原因有:
A、复制-粘贴编程手法
B、即兴维护
C、交流不畅

举例说明:

  • 问题点:
    java.util.Date和其派生类java.sql.Date同名,这两个类位于不同的包中,编译器不会因为它们同名而报错,但这让使用者一头雾水,这样将导致二义性。

  • 解决方案
    将Date名称前面加上用途限定语,比如java.sql.SQLDate更合适。

三、小结

由于内容太多,我们在第一部分只介绍抽象型设计原则,接下来我将继续写模化型设计原则,封装型设计原则和层次化设计原则,与大家深入讨论从设计角度来看,什么样的代码才是真正的好代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,831评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • 文/夏彼得 埃里克·里斯在其著作《精益创业》中提出了“最小可行产品”(Minimum Viable Product...
    夏彼得阅读 2,219评论 0 3
  • 在今日头条里面经常看到有许多小编在抱怨,自己的头条号还在新手期,每天坚持原创,感觉灵感都要用光了。刚开始我也是深有...
    语见生活阅读 155评论 1 0