zoukankan      html  css  js  c++  java
  • 重构如何进行?

    注:本篇文章是结合自己的体会和《重构》来完成的,如果各位对本文没啥兴趣,强烈建议读一读《重构》,因为本文到处都充斥(此处为褒义)着《重构》的气味。

    在日常的代码书写过程中,虽然有项目需求的参照,但是我们的代码还是要有个生产过程,很少有大师级的人物对要求的代码一蹴而就。我们总是要随着需求的演进,不断地改进我们的代码,不断地调整代码之间的调用方式,类的关联等等各个方面。那如何在写代码的过程中,实施重构,有哪些重构方法可以实施呢?从以下几个方面,我们来大体阐述一下,重构的产生,方法及其实施:

    1.如何开始项目;

    2.重构的前期准备;

    3.实施方法;

    4.参考资料。

    一、如何开始项目:

    在接到任务时,要先考虑一下设计思路,但是因为对uml不是很熟悉,尤其是不想通过那些关系图来说明问题,前期的考虑结果也多数情况下会变成一张画满图形关联的草稿纸。于是乎,动手。这没什么特别之处,各位肯定也是这么做的。当年看过一篇文章,作者说自己有了多年的软件开发经验,能够针对既存的代码提出改进建议和意见,但是当要自己开始一个新的项目时,总是不知道该如何下手。那么我前面说的这样的开始,各位同僚可能也会质疑:是不是太简单了?好吧,我们继续深入。我会按照最开始的设计思路,实现一部分代码,当以代码形式展现你的思想时,你一定要把需求跟设计关联在一起,而且要稍微有点前瞻性,不要太过,否则过犹不及,会造成过度设计。好吧,那么我最开始的设计多数是这种情况:对于一个类而言,里面有一到两个方法。对于复杂功能来说,这是很危险的,如果你在纠结中实现了这个需求,你可能会有一种呕吐的感觉,使得你自己都不愿意再回头看你原来的代码。这个时候,所谓的代码“臭味”就会产生,耦合的设计,邋遢的类和冗长的方法。。。。。一般情况下,我会忍住对这种好不容易揉和出来的代码的反感,多看几眼这个方法。然后采取三个措施:抽取方法,抽取方法,抽取方法。。。

    我的第一步就是抽取方法。

    先别急,Fowler也建议说:一次进行一小步。这个很重要,因为对于我们好不容易“弄”出来的代码,太有可能一下子就超过了50行,超过五十行的代码里面一般情况下会掩盖几个方面的信息:业务耦合,分配不均,职责不明,最关键的是没有针对各种业务添加主要负责人。抽取方法就是要划分业务处理,或者是动作处理。所以,经过第一次处理之后,我们的主要方法可能就被四分五裂了,但是,你的收获是:一个主要而清晰的方法,跟了一帮短小精悍的小方法(其实不一定短小精悍,但是肯定比原来清晰)。因为做java开发,几个ide工具基本都有相应的代码重构处理,不管是eclipse还是intellij都有抽取方法的功能。接下来再观察一下,在某个抽取出来的方法里面,其任务主要是在构造一个对象,这个时候,你又可以动手了:抽取builder对象。

    那么第二步,就是抽取代理对象。

    如何抽取呢?在第一步的基础上,我们已经得到了处理一个业务的各个子方法,也就是说,我们把原来的业务进行了划分。在这个时候,我们应该再对照一下需求文档,看我们的划分是否“合理”。这里的“合理”验证应该是分析我们的设计是否能够满足需求的进化,也就是稍微前瞻一点的设计(再次提醒:适可而止!),觉得可以满足,需求点里面的哪些变化,这些估计可以跟开发同事一起讨论,也可以qa或者需求管理员讨论。不仅仅可以使得自己对需求有更加深入的理解,也可以使设计思路慢慢清晰起来。好了,现在我们发现抽取出来的方法可以做这样的划分:对象准备、业务处理、验证方法、工具方法、结果处理、异常处理等几个方面(可能还有很多其他方面,各位有的就补充一下,一起分析)。

    (一)考虑处理对象准备的方法:

    如果此类方法比较复杂,我会考虑将其变成一个类来处理。比如利用设计模式中的工厂、构造器等方式,那么我们就增加了一系列的创建类(重构增加了类的数量和关联复杂度,但使得代码结构更加清晰)。当然,我们不一定要抽取出各种工厂,构造器等类,因为这些类抽取出来后,是为以后的扩展做准备。因为结合需求,我们发现跟当前准备的对象同类的会越来越多,所以这种工厂和构造器就使得以后的扩展变得轻松了。但是,如果我们紧紧是为了抽取出这个准备方法,使它能独立的在原来类的范围内使用,那可以做一个内部类。这些都要依据用户需求才行,不能臆断。当然,如果这个类很想,你就别费这个时间和精力,非要弄出一个工厂类来,那反倒是过度设计了。。。

    (二)业务处理方法:

    业务处理方法是很容易集聚你的业务领域内容的。个人认为,如果能够跟需求管理员一起就此部分深入讨论,就能够达到领域驱动所要求那些领域定义对象(强调一遍:业务专用对象)。这些业务处理方法,在抽取成类时,命名很重要,要完全体现业务需求的主要内容,而且这个时候,可能要伴随java包的重构,是的业务处理更加具有内聚性。这个时候,我也会考虑一些设计模式,比如facade、state、strategy等等,这个里面基本上可以用上所有结构和行为方面的设计模式。就是说不要因为你抽取出了业务处理对象,并迁移到domain包内,而使得你的业务处理包有很多依赖入口。

    (三)验证方法:

    因为验证总是一个重复使用的东西,我们可以通过不断地抽取验证方法,发现我们会有哪些重复动作。一次在写一个关于java容器中的查询功能时,发现泛型可以使得接口变得相当通用(这是泛型的一个很重要的作用,平常没怎么用而已),但是加了泛型之后,我却不了解具体类型了,也就不能特别验证特别的对象了,进而使得我的方法增加了很多验证的静态代码,于是乎,抽之。抽出这个方法之后,我就可以做多方面处理了:1.类型数量不多,我就写死(好代码也是从垃圾代码产生的)。2.随着种类的增多,我就根据strategy或state模式增加相应验证。

    一般情况下,验证方法可能仅仅是某个类或者方法内部使用,但是很多情况下,这种验证可以抽取出来共用,就如同工具方法一样。

    (四)工具方法:

    其实上面的验证方法也可以成为一种工具方法,我们看spring等开源代码时,会看到很多工具类,都是在spring-core的util包里面,如:ClassUtils、FileCopyUtils等。所以,我们在这里抽出来的方法,也可以根据不同的作用慢慢积累起来。这都是最原生的方式,spring肯定也不是一下子就写出这么写工具类。我们的私用api也可以日积月累啦。

    (五)异常处理:

    异常是java里面很重要的一个组成。只要跟资源相关,就要用到异常处理。所以,在这里我也是把它独立,希望可以在这里特别的对其作出一个说明。以避免不必要的分层级别上,管理异常。在我们重构的过程中,我们要明确到底在哪一层上对异常进行处理。这一点,还是单开一篇文章吧,如果大家等不及,可以看看《Expert one on one j2ee design and development》。

    经过以上的处理,个人觉得,我们的项目已具雏形了。

    其实,我们在开始一个项目的时候,难免迷茫,万事开头难也体现在这里。即便有很多成熟的框架摆在我们的面前,但不是每个框架都是你的那盘菜。框架准备好了,还不够,如果你想用老三段:M-V-C.那你肯定驾轻就熟,但是仅仅有了这些框架不一定就说明我们一定能写出好项目。从一开始就借助重构不断地改进我们的代码是极其必要的。

    二、前期准备

    重构的前期准备,主要是测试环境的搭建。一旦决定重构,就要确保一个前提,那就是原来的功能不能被破坏掉。如何确保原来的功能依然可用?测试。而且肯定不能依赖一个简单main函数来测试一下,尽管这可以实现一定的处理,总是不太方便扩展及用例的增加等等。我们只是简单的说明一下单元测试,以junit为例吧。

    我接触的项目都不是啥大项目,即便是个大项目,也可以用这样的框架来做:spring-context、core、bean等作为基础,spring mvc作为前端。但是在测试方面,尤其是开发的单元测试也就离不开junit了。

    我在测试时,是将spring跟junit结合在一起的,用到了spring-test。所以,可以将spring的context和junit结合在一起。这些东西都有了,前期的准备也就差不多了,junit大家就去找点资料吧,因为它的使用还是比较简单的,关键还是要写出有意义的测试代码。

    三、实施方法

    (备注:以下内容可能跟《重构》里面有极大的相似之处,各位有理由怀疑,但是咱绝对没那闲工夫抄袭,起码也要算是自己的理解,所以,发现任何问题,望不吝赐教!)
    关于重构,都有哪些具体的实施方法呢?首先要说明一点,那就是进行任何重构之前,一定要先准备测试代码,保证原来功能的准确性,或者保证整体功能的准确性。这个相当重要,除非你完全保证,但是一般情况下,别去除非。

    我们主要从几个方面来说明吧:

    (一)基于函数的处理

    1)抽取函数

    在《重构》中对这个部分做了很细致的说明。主要原因我想可能是他们没有使用现代点的IDE,上面说了,不管是eclipse还是intellij都有很强大的方法抽取功能。以intellij为例,其重构的功能就有抽取方法,抽取方法类(即:将一段代码转换成一个对象的方法调用),抽取类(即:将一段代码抽取出来作为一个类,并在原来代码中增加对这个类的依赖,把原来的那段代码换成对依赖对象方法的调用),替换重复的代码等等,功能相当强大,鉴于本文不是intellij的说明书,介绍到此结束。言归正传,我们要使用抽取方法时,尤其要考虑变量的问题,也就是我们所关心的代码片段中的变量问题。变量问题分为几种情况:无局部变量,这个时候可以直接将这段代码抽出来,定义成一个函数,然后将原来的代码片段替换为方法引用;有局部变量,这里就比较复杂了,还要进一步划分为无需修改值的情况和修改变量值的情况。对于第一种情况,是说我们的代码片段仅仅是使用变量,于是我们可以把这个变量变成输入参数来用;对于第二种情况,没办法,还要进一步划分为:

  • 相关阅读:
    三数之和
    罗马数字与整数
    Oracle 开启或关闭归档
    Oracle RMAN scripts to delete archivelog
    Oracle check TBS usage
    Oracle kill locked sessions
    场景9 深入RAC运行原理
    场景7 Data Guard
    场景4 Data Warehouse Management 数据仓库
    场景5 Performance Management
  • 原文地址:https://www.cnblogs.com/ericchen/p/2127506.html
Copyright © 2011-2022 走看看