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

前言

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

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

一、技术债务

何为技术债务?

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

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

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

二、设计的坏味道

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

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

  • 可理解性

    代码理解起来的难易程度

  • 可修改性

    在修改既有功能时,不会导致连锁反应。

  • 可扩展性

    支持新功能,不会导致连锁反应

  • 可重用性

    可以在代码的其他地方引用其一块代码

  • 可测试性

    项目要能够支持单元测试

  • 可靠性

    在正确的实现了功能的同时,也能够考虑各种异常情况如何容错

2.1、设计坏味的分类

2.1.1 抽象型坏味道

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、命令式抽象

举例如下:

  • 问题点:

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

  • 解决方案

    注:根据高内聚原则,统一归集到一个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更合适。

三、小结

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

时间: 2024-11-08 22:21:50

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

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

我们接第一篇由学习<软件设计重构>所想到的代码review(一) 来继续说明在代码review中,有哪些属于"层次结构"中的坏味道. 注:通过上图咱们看到了在层次结构中有九大问题点,咱们就从中找出三个典型的问题点给与分析和解释. 一.缺失的层次结构 问题点: public Insets getBorderInsets(Component c, Insets insets) { if(c instanceof AbstractButton) { margin = ((Abst

重构——让你的代码接近框架源码

前一段我们的项目搞了一次重构,我简单做了一个ppt,下面我们来一起分享下 代码的坏味道 1.重复代码(难维护) ?提取公共函数 2.函数过长(难理解) ?拆成若干函数 3.类过大(难理解) ?拆成若干类 4.参数多(难用) ?将参数封装成结构或类 5.万能类(改动频繁) ?拆,将总是一起变化的东西放在一块儿,合久必分 6.天女散花逻辑(需求变动改很多类) ?将各个修改点,集中起来,抽象成一个新类. 7.红杏出墙的函数(使用了大量其他类的成员) ?将这个函数挪到那个类里面. 8.数据团(常一起出现

软件设计与实现总结

本周学习了<软件设计与实现>的章节,了解了一些常用的分析和设计方法和开发阶段的一些管理方法: 1.分析和设计方法: 写软件就是为了解决用户的需求,所以我们首先了解用户需求即需求分析. 方法:(1)以文字为主的文档(2)以图形为主的构造模型(3)数学语言(4)类+代码(5)源代码+注释 2.从Spec到实现 (1)估计开发任务所需时间(2)分析需求(3)生成设计文档(4)和同事审核文档(5)编写代码(6)代码复审,代码重构 3.开发人员的标准工作流程(附图片) BTV测试又称冒烟测试 4.开发阶

学习软件开发应该看的书

学习软件开发应该看的书 来源: 李越甲的日志 1. C语言提升 <c和指针> <c缺陷和陷阱> <c专家编程> 这三本书是一个初级C程序员必看的三本书,同时也是应届新员工必须好好学习的教材,非常适合刚毕业的大学生学习 2.  C++语言提升 <Effective C++:改善程序与设计的55个具体做法> <More Effective C++:35个改善编程与设计的有效方法(中文版)> <C++沉思录> <C++Template

《重构-改善既有代码的设计》读书笔记

重构,第一个案例 1.1 起点 如果发现现有的代码结构使你无法很方便地添加新特性,那就先重构,使特性的添加比较容易进行后,再添加特性; 1.2 重构的第一步 为即将修改的代码建立可靠的测试环境 – 是人就会犯错,所以需要可靠的测试; 测试结果能够自我检验 – 成功"OK",失败列出失败清单并打印行号 (自动化对比测试结果是提高效率的前提); 1.3 分解并重组"巨型"函数 切分提炼长函数(Extract Method),并移至更合适的类(Move Method) –

一些软件设计原则【转载】

本文一定要转,总结得非常好, 设计必读. 转自陈皓老师的 <一些软件设计的原则>,根据自己的理解调整了下顺序,少部分字句做了修改. 一个好的程序员通常由其操作技能.知识水平,经验层力和能力四个方面组成.在这里想和大家说说设计中的一些原则,我认为这些东西属于长期经验总结出来的知识.这些原则,每一个程序员都应该了解.但是请不要教条主义,在使用的时候还是要多多考虑实际情况.其实,下面这些原则,不单单只是软件开发,可以推广到其它生产活动中,甚至我们的生活中. 根本设计原则 根本设计原则是我认为的最最基

实践提高《重构改善既有代码的设计第2版》PDF中文+PDF英文+对比分析

重构是编程的基础,是在不改变外部行为的前提下,有条不紊地改善代码.编程爱好者都知道,Martin Fowler 的<重构:改善既有代码的设计>已经成为全球有经验的程序员手中的利器,既可用来改善既有代码的设计.提升软件的可维护性,又可用于使既有代码更易理解.焕发出新的活力. <重构改善既有代码的设计(第2版)>在第1 版的基础上做了全面修订,反映了编程领域业已发生的许多变化.第2 版中介绍的重构列表更加内聚,并用JavaScript 语言重写了代码范例.此外,第2 版中还新增了与函数

软件设计

软件设计 一定是创建订单的时候填充market字段,我曾经一度打算在回调的时候再根据回调方来填充Market,但是如果没有回调呢?Market这样的标志性字段一定要依赖于靠谱的操作: 对于重载方法要注意,尤其套调用的重载方法,对于某些核心校验必须要放置在里层方法调用,否则因为重载都是public出去的,都可以被外界调用,如果在外层方法实现校验,里层重载方法被外界直接调用,校验会被跳过:考虑CheckMarket是放在CreateOrder(String encryptedString)还是Cre

软件设计原则和方法通俗理解

网上有很多关于软件设计原则的说法,很精确,很官方,但是对于有些初学者来说可能是不知所云,到最后把自己给郁闷到了,学习软件应该是一件愉快的事情. 那么软件设计原则有哪些呢? (1)可靠性 做出一个可靠的软件,跟女人找一个可靠的男人一样,女人找男人,需要男人品质好,人品好,靠谱,可信赖,可依靠,身材高大,等等.软件设计也是一样,在软件的设计阶段就要非常注意软件的可靠性,不要等到最后用的时候发现软件这里不行那里不行,或者说在使用软件过程中一旦发现问题还是可以恢复使用,不能直接崩溃. (2)健壮性 这个