`
381895649
  • 浏览: 230344 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

java 代码重构-第一章(去除临时变量)

    博客分类:
  • java
 
阅读更多

上一篇文章:java 代码重构-第一章(提炼代码)
说到了代码的提炼,把一些代码提炼出来写成一个方法,然后再去调用它,好了不多说了,想了解看上一篇吧

 

 

去除临时变量

正如我在前面提过的,临时变量可能是个问题。它们只在自己所属的函数中有效,所以它们会助长「冗长而复杂」的函数。这里我们有两个临时变量,两者都是用来从Customer 对象相关的Rental 对象中获得某个总量。不论ASCII 版或HTML 版都需要这些总量。我打算运用 Replace Temp with Query,并利用所谓的query method 来取代totalAmount 和frequentRentalPoints 这两个临时变量。由于class 内的任何函数都可以取用(调用)上述所谓query methods ,所以它能够促进较干净的设计,而非冗长复杂的函数:

首先:我们看回旧的代码

 

class Customer {
    public String statement() {
        double totalAmount = 0;
        int frequentRenterPoints = 0;
        Enumeration rentals = _rentals.elements();
        String result = "Rental Record for " + getName() + "\n";
        while (rentals.hasMoreElements()) {
            Rental each = (Rental) rentals.nextElement();
            frequentRenterPoints += each.getFrequentRenterPoints();
            // show figures for this rental
            result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n";
            totalAmount += each.getCharge();
        }
        // add footer lines
        result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
        result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points";
        return result;
    }
}
 

 

我以Customer class 的getTotalCharge() 取代totalAmount 。

代码变成下面:

 

class Customer {
...
    /**
     * 通计清单
     * 
     * @return
     */
    public String statement() {
        int frequentRentePoints = 0;
        Enumeration<Rental> enu_rentals = rentals.elements();
        String result = "Rental Record for " + this.getName() + " \n";
        while (enu_rentals.hasMoreElements()) {
            Rental each = enu_rentals.nextElement();
            frequentRentePoints += each.getFrequentRenterPoints();
            result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n";
        }
        result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
        result += "You earned " + String.valueOf(frequentRentePoints) + " frequent renter points";
        return result;
    }

    // 译注:此即所谓query method
    private double getTotalCharge() {
        double result = 0;
        Enumeration<Rental> enu_rentals = rentals.elements();
        while (enu_rentals.hasMoreElements()) {
            Rental each = (Rental) enu_rentals.nextElement();
            result += each.getCharge();
        }
        return result;
    }

}

 重构之后,重新编译并测试,然后以同样手法处理frequentRenterPoints。

 

class customer ...
public String statement() {
    int frequentRenterPoints = 0;
    Enumeration rentals = _rentals.elements();
    String result = "Rental Record for " + getName() + "\n";
    while (rentals.hasMoreElements()) {
        Rental each = (Rental) rentals.nextElement();
        frequentRenterPoints += each.getFrequentRenterPoints();
        //show figures for this rental
        result += "\t" + each.getMovie().getTitle()+ "\t" +

    }
    //add footer lines
    result +=  "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
    result += "You earned " + String.valueOf(frequentRenterPoints) +
      " frequent renter points";
    return result;
}
 

 

 

 

添加方法代码如下:

 

class customer ...
/**
 * 通计清单
 * 
 * @return
 */
public String statement() {
    Enumeration<Rental> enu_rentals = rentals.elements();
    String result = "Rental Record for " + this.getName() + " \n";
    while (enu_rentals.hasMoreElements()) {
        Rental each = enu_rentals.nextElement();
        result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n";
    }
    result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
    result += "You earned " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points";
    return result;
}

// 此即所谓query method
private double getTotalCharge() {
    double result = 0;
    Enumeration<Rental> enu_rentals = rentals.elements();
    while (enu_rentals.hasMoreElements()) {
        Rental each = (Rental) enu_rentals.nextElement();
        result += each.getCharge();
    }
    return result;
}

// 此即所谓query method
private int getTotalFrequentRenterPoints() {
    int result = 0;
    Enumeration<Rental> enu_rentals = rentals.elements();
    while (enu_rentals.hasMoreElements()) {
        Rental each = (Rental) enu_rentals.nextElement();
        result += each.getFrequentRenterPoints();
    }
    return result;
}

 

 

图1.8至图1.11分别以UML class diagram(类图)和interaction diagram (交互作用图)展示statement() 重构前后的变化。

对比

 

 

想一想:

做完这次重构,有必要停下来思考一下。大多数重构都会减少代码总量,但这次却增加了代码总量,那是因为Java 1.1需要大量语句(statements)来设置一个总和(summing)循环。哪怕只是一个简单的总和循环,每个元素只需一行代码,外围的支持代码也需要六行之多。这其实是任何程序员都熟悉的习惯写法,但代码数量还是太多了。

这次重构存在另一个问题,那就是性能。原本代码只执行while 循环一次,新版本要执行三次。如果循环耗时很多,就可能大大降低程序的性能。单单为了这个原因,许多程序员就不愿进行这个重构动作。但是请注意我的用词:如果和可能。除非我进行评测(profile),否则我无法确定循环的执行时间,也无法知道这个循环是否被经常使用以至于影响系统的整体性能。重构时你不必担心这些,优化时你才需要担心它们,但那时候你已处于一个比较有利的位置,有更多选择可以完成有效优化

现在,Customer class 内的任何代码都可以取用这些query methods 了,如果系统他处需要这些信息,也可以轻松地将query methods 加入Customer class 接口。如果没有这些query methods ,其他函数就必须了解Rental class,并自行建立循环。在一个复杂系统中,这将使程序的编写难度和维护难度大大增加。

你可以很明显看出来,htmlStatement() 和statement() 是不同的。现在,我应该脱下「重构」的帽子,戴上「添加功能」的帽子。我可以像下面这样编写htmlStatement() ,并添加相关测试。

htmlStatement代码

 

class sustumer...
/**
 * 输出html清单
 * 
 * @return
 */
public String htmlStatement() {
    Enumeration<Rental> enu_rentals = rentals.elements();
    String result = "<H1>Rentals for <EM>" + getName() + "</EM></ H1><P>\n";
    while (enu_rentals.hasMoreElements()) {
        Rental each = (Rental) enu_rentals.nextElement();
        // show figures for each rental
        result += each.getMovie().getTitle() + ": " + String.valueOf(each.getCharge()) + "<BR>\n";
    }
    // add footer lines
    result += "<P>You owe <EM>" + String.valueOf(getTotalCharge()) + "</EM><P>\n";
    result += "On this rental you earned <EM>" + String.valueOf(getTotalFrequentRenterPoints())
            + "</EM> frequent renter points<P>";
    return result;
}

 通过计算逻辑的提炼,我可以完成一个htmlStatement() ,并复用(reuse)原本statements() 内的所有计算。我不必剪剪贴贴,所以如果计算规则发生改变,我只需在程序中做一处修改。完成其他任何类型的报表也都很快而且很容易。这次重构并不花很多时间,其中大半时间我用来弄清楚代码所做的事,而这是我无论如何都得做的。

 

前述有些重构码系从ASCII 版本里头拷贝过来——主要是循环设置部分。更深入的重构动作可以清除这些重复代码。我可以把处理表头(header)、表尾(footer)和报表细目的代码都分别提炼目出来。在 Form Template Method 实例中,.你可以看到如何做这些动作。但是,现在用户又开始嘀咕了,他们准备修改影片分类规则。我们尚未清楚他们想怎么做,但似乎新分类法很快就要引入,现有的分类法马上就要变更。与之相应的费用计算方式和常客积点计算方式都还待决定,现在就对程序做修改,肯定是愚蠢的。我必须进入费用计算和常客积点计算中,把「因条件 而异的代码」(译注:指的是switch 语句内的case 子句)替换掉,这样才能为 将来的改变镀上一层保护膜。现在,请重新戴回「重构」这顶帽子。

那么下面是完整代码:

Customer

 

package com.mkfree.refactoring.shap1;

import java.util.Enumeration;
import java.util.Vector;

/**
 * 顾客
 * 
 * @author hk
 * 
 *         2012-12-25 下午10:59:03
 */
public class Customer {

    private String name;
    private Vector<Rental> rentals = new Vector<>();

    public Customer(String name) {
        this.name = name;
    }

    /**
     * 添加
     * 
     * @param rental
     */
    public void addRentals(Rental rental) {
        rentals.add(rental);
    }

    public String getName() {
        return name;
    }

    /**
     * 输出html清单
     * 
     * @return
     */
    public String htmlStatement() {
        Enumeration<Rental> enu_rentals = rentals.elements();
        String result = "<H1>Rentals for <EM>" + getName() + "</EM></ H1><P>\n";
        while (enu_rentals.hasMoreElements()) {
            Rental each = (Rental) enu_rentals.nextElement();
            // show figures for each rental
            result += each.getMovie().getTitle() + ": " + String.valueOf(each.getCharge()) + "<BR>\n";
        }
        // add footer lines
        result += "<P>You owe <EM>" + String.valueOf(getTotalCharge()) + "</EM><P>\n";
        result += "On this rental you earned <EM>" + String.valueOf(getTotalFrequentRenterPoints())
                + "</EM> frequent renter points<P>";
        return result;
    }

    /**
     * 通计清单
     * 
     * @return
     */
    public String statement() {
        Enumeration<Rental> enu_rentals = rentals.elements();
        String result = "Rental Record for " + this.getName() + " \n";
        while (enu_rentals.hasMoreElements()) {
            Rental each = enu_rentals.nextElement();
            result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n";
        }
        result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
        result += "You earned " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points";
        return result;
    }

    // 此即所谓query method
    private double getTotalCharge() {
        double result = 0;
        Enumeration<Rental> enu_rentals = rentals.elements();
        while (enu_rentals.hasMoreElements()) {
            Rental each = (Rental) enu_rentals.nextElement();
            result += each.getCharge();
        }
        return result;
    }

    // 此即所谓query method
    private int getTotalFrequentRenterPoints() {
        int result = 0;
        Enumeration<Rental> enu_rentals = rentals.elements();
        while (enu_rentals.hasMoreElements()) {
            Rental each = (Rental) enu_rentals.nextElement();
            result += each.getFrequentRenterPoints();
        }
        return result;
    }

}
 

 

 

Movie

 

package com.mkfree.refactoring.shap1;

/**
 * 电影类
 * @author hk
 *
 * 2012-12-25 下午10:55:14
 */
public class Movie {

    public static final int CHILDRENS = 2;
    public static final int REGULAR = 0;
    public static final int NEW_RELEASE = 1;

    private String title;
    private int priceCode;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public int getPriceCode() {
        return priceCode;
    }
    public void setPriceCode(int priceCode) {
        this.priceCode = priceCode;
    }

}

 

 

Rental

 

package com.mkfree.refactoring.shap1;

/**
 * 租凭
 * 
 * @author hk
 * 
 *         2012-12-25 下午10:57:00
 */
public class Rental {

    private Movie movie;
    private int daysRented;

    public Rental(Movie movie, int daysRented) {
        this.movie = movie;
        this.daysRented = daysRented;
    }

    public Movie getMovie() {
        return movie;
    }

    public int getDaysRented() {
        return daysRented;
    }

    double getCharge() {
        double result = 0;
        switch (getMovie().getPriceCode()) {
        case Movie.REGULAR:
            result += 2;
            if (getDaysRented() > 2)
                result += (getDaysRented() - 2) * 1.5;
            break;
        case Movie.NEW_RELEASE:
            result += getDaysRented() * 3;
            break;
        case Movie.CHILDRENS:
            result += 1.5;
            if (getDaysRented() > 3)
                result += (getDaysRented() - 3) * 1.5;
            break;
        }
        return result;
    }

    /**
     * 
     * @return
     */
    int getFrequentRenterPoints() {
        if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1)
            return 2;
        else
            return 1;
    }
}

 

 Client

 

package com.mkfree.refactoring.shap1;

import org.junit.Test;

public class Client {

    @Test
    public void testStatement() {

        Movie movie1 = new Movie();
        movie1.setTitle("少林足球");
        movie1.setPriceCode(1);
        Rental rental1 = new Rental(movie1, 2);

        Movie movie2 = new Movie();
        movie2.setTitle("大话西游");
        movie2.setPriceCode(2);
        Rental rental2 = new Rental(movie2, 3);

        Customer customer = new Customer("oyhk");
        customer.addRentals(rental1);
        customer.addRentals(rental2);
        String statement = customer.statement();
        System.out.println(statement);
        System.out.println("------------------------------------------------");
        String htmlStatment = customer.htmlStatement();
        System.out.println(htmlStatment);
    }
}

 

 

最后输出的结果:有两个 1 statement 2 htmlStatement

  • 大小: 14.2 KB
  • 大小: 19.7 KB
  • 大小: 15.2 KB
  • 大小: 25.8 KB
分享到:
评论

相关推荐

    .java代码重构

    在Java编程中,代码重构是一种常见的实践,尤其在大型项目中,为了保持代码的健康状态和持续优化,重构是必不可少的。 1. **重构的目的** - 提高代码质量:通过消除冗余代码,简化复杂的逻辑,使得代码更加清晰...

    java代码重构一到六章

    ### Java代码重构:掌握优化现有代码的艺术 #### 引言 在软件开发的过程中,随着项目的不断演进,代码库往往会出现复杂度增加、可读性和可维护性下降的问题。这时,进行代码重构变得至关重要。重构是指在不改变...

    重构-改善既有代码的设计

    第1章 重构,第一个案例 1 1.1 起点 1 1.2 重构的第一步 7 1.3 分解并重组statement() 8 1.4 运用多态取代与价格相关的条件逻辑 34 1.5 结语 52 第2章 重构原则 53 2.1 何谓重构 53 2.2 为何重构 ...

    重构-第3章 代码的坏味道-读书笔记

    《重构-第3章 代码的坏味道》是软件开发领域的一本经典著作,由Martin Fowler所著。这本书深入探讨了如何识别并消除代码中的不良设计模式,以提高代码质量、可读性和可维护性。在第三章中,作者详细列举了多种"代码...

    java代码重构经验分享

    Java 代码重构经验分享 Java 代码重构是指在不改变外部行为的情况下,修改代码的内部结构,以提高代码的可维护性、可读性和可扩展性。本文总结了 Java 代码重构的经验和技术规范,包括重构要求、重构的工作、代码的...

    一本介绍如何将现有的 Java 代码重构为 Kotlin 代码的书籍

    本书《从Java到Kotlin:重构指南》由Duncan McGregor与Nat Pryce共同编写,是一本专为已经熟悉Java编程语言并希望将其现有代码转换为Kotlin的开发者准备的书籍。书中不仅提供了丰富的重构技巧,还包括了实用的示例和...

    java代码重构经验总结

    ### Java代码重构经验总结 在软件开发过程中,代码重构是一项重要的技能,它旨在不改变代码外部行为的前提下,改进其内部结构,从而提升代码质量和可维护性。本文将深入探讨Java代码重构的关键点,涵盖重构原则、...

    重构--改善既有代码的设计_中文版

    java开发经典书籍,重构--改善既有代码的设计_中文版 java开发经典书籍,重构--改善既有代码的设计_中文版 java开发经典书籍,重构--改善既有代码的设计_中文版

    《重构——改善既有代码的设计》第一个案例代码

    6. **消除临时变量**: 在某些情况下,临时变量的存在只是为了传递数据,这种情况下可以考虑使用函数引用来直接传递,减少变量的使用。 7. **使用断言**:在关键位置添加断言,可以帮助在开发过程中尽早发现错误,...

    重构-改善既有代码的设计.pdf

    重构-改善既有代码的设计.pdf重构-改善既有代码的设计.pdf

    Java 代码重构示例

    Java代码重构示例 Java代码重构示例 Java代码重构示例 Java代码重构示例 Java代码重构示例 Java代码重构示例 Java代码重构示例

    Java 代码重构实例

    Java代码重构是一种优化编程实践,旨在改进代码的结构和可读性,而不改变其外部行为。重构对于提高软件质量和维护性至关重要,尤其是在大型项目中。以下是一些在Java重构中的关键原则和技巧,通过实例来展示如何应用...

    java代码重构圣经

    java代码重构以前忽视了,最近在看 字字珠玑,相见恨晚

    代码重构-重构是设计演进的基本方法

    软件设计是演进过程,而重构是设计演进的基本方法。重构是指不改变软件行为的前提下,修改程序内部结构。重构说简单,做不简单。首先,需要知道代码的好坏,即代码异味,设计原则等。其次,需要以自动测试作为保障。

    java代码重构

    Java代码重构是一个重要的软件开发实践,它涉及到对现有代码的改进,目的是提高代码的可读性、可维护性和整体质量,而不改变其外在行为。重构对于大型项目尤其关键,因为随着时间的推移,代码可能会变得复杂且难以...

    java-8-openjdk-amd64

    Java 8 OpenJDK AMD64 是一个针对64位AMD架构的开源Java开发和运行环境,由OpenJDK项目提供。OpenJDK是Java Development Kit(JDK)的一个实现,它遵循GNU General Public License(GPL)条款,旨在提供一个免费、...

    重读martin大师的《重构--提高既有代码的设计》第一章

    在压缩包文件"src"中,通常包含了与《重构》第一章相关的示例代码或练习素材。读者可以通过分析和实践这些代码,更好地理解和应用书中的重构技巧。 总之,Martin Fowler的《重构--提高既有代码的设计》第一章为我们...

    JavaParser java-symbol-solver 使用官方文档

    这一过程对于诸如静态代码分析、代码生成、代码重构等应用场景尤为重要。 #### Abstract Syntax Tree (AST) 当JavaParser处理Java源代码时,它会创建一个称为抽象语法树的数据结构。AST是一种树形结构,其中每个...

Global site tag (gtag.js) - Google Analytics