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

java 代码重构-第一章(类自己该做自己的事)

    博客分类:
  • java
阅读更多

 

重构小提示:重构技术系以微小的步伐修改程序。如果你犯下错误,很容易便可发现它。

 

上一篇文章:java 代码重构-第一章(分解并重组statement())

下一篇文章:java 代码重构-第一章(提炼代码)

 

上次重构了statement(),类看起来,感觉舒服了很多是吧?那下面我们再一步步的做下去...

 

 

现在,我已经把原本的函数分为两块,可以分别处理它们。我不喜欢amountFor() 内的某些变量名称,现在是修改它们的时候。

下面是原本的代码。

private int amountFor(Rental each) { // 计算一笔租片费。
    int thisAmount = 0;
    switch (each.getMovie().getPriceCode()) {
    case Movie.REGULAR: // 普通片
        thisAmount += 2;
        if (each.getDaysRented() > 2)
            thisAmount += (each.getDaysRented() - 2) * 1.5;
        break;
    case Movie.NEW_RELEASE: // 新片
        thisAmount += each.getDaysRented() * 3;
        break;
    case Movie.CHILDRENS: // 儿童。
        thisAmount += 1.5;
        if (each.getDaysRented() > 3)
            thisAmount += (each.getDaysRented() - 3) * 1.5;
        break;
    }
    return thisAmount;
}
 

下面是易名后的代码:

 

private double amountFor(Rental aRental) { // 计算一笔租片费。
    double result = 0;
    switch (aRental.getMovie().getPriceCode()) {
    case Movie.REGULAR: // 普通片
        result += 2;
        if (aRental.getDaysRented() > 2)
            result += (aRental.getDaysRented() - 2) * 1.5;
        break;
    case Movie.NEW_RELEASE: // 新片
        result += aRental.getDaysRented() * 3;
        break;
    case Movie.CHILDRENS: // 儿童。
        result += 1.5;
        if (aRental.getDaysRented() > 3)
            result += (aRental.getDaysRented() - 3) * 1.5;
        break;
    }
    return result;
}
 

 

下面是重构后,整个类的代码

 

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;
    }

    /**
     * 通计清单
     * 
     * @return
     */
    public String statement() {
        double totalAmount = 0;// 合计
        int frequentRentePoints = 0;
        Enumeration<Rental> enu_rentals = rentals.elements();
        String result = "Rental Record for " + this.getName() + " \n";
        while (enu_rentals.hasMoreElements()) {
            double thisAmount = 0;
            Rental each = enu_rentals.nextElement();

            thisAmount = amountFor(each);// 计算一笔租片费

            frequentRentePoints++;

            if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) {
                frequentRentePoints++;
            }

            result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(thisAmount) + "\n";

            totalAmount += thisAmount;

        }
        result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
        result += "You earned " + String.valueOf(frequentRentePoints) + " frequent renter points";
        return result;
    }

    /**
     * 把计算一笔租片
     * 
     * @param each
     * @return
     */
    private double amountFor(Rental aRental) { // 计算一笔租片费。
        double result = 0;
        switch (aRental.getMovie().getPriceCode()) {
        case Movie.REGULAR: // 普通片
            result += 2;
            if (aRental.getDaysRented() > 2)
                result += (aRental.getDaysRented() - 2) * 1.5;
            break;
        case Movie.NEW_RELEASE: // 新片
            result += aRental.getDaysRented() * 3;
            break;
        case Movie.CHILDRENS: // 儿童。
            result += 1.5;
            if (aRental.getDaysRented() > 3)
                result += (aRental.getDaysRented() - 3) * 1.5;
            break;
        }
        return result;
    }
}

 

易名之后我需要重新编译并测试,确保没有破坏任何东西。

更改变量名称是值得的行为吗?绝对值得。好的代码应该清楚表达出自己的功能,变量名称是代码清晰的关键。如果为了提高代码的清晰度,需要修改某些东西的名字,大胆去做吧。

 

重构小提示:任何一个傻瓜都能写出计算机可以理解的代码。惟有写出人类容易理解的代码,才是优秀的程序员。

 

 

代码应该表现自己的目的,这一点非常重要。阅读代码的时候,我经常进行重构。这样,随着对程序的理解逐渐加深,我也就不断地把这些理解嵌入代码中,这么一来才不会遗忘我曾经理解的东西。

搬移「金额计算」代码

观察amountFor() 时,我发现这个函数使用了来自Rental class 的信息,却没有使 用来自Customer class 的信息。

 

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

 

 这立刻使我怀疑它是否被放错了位置。绝大多数情况下,函数应该放在它所使用的数据的所属object(或说class)内,所以amountFor() 应该移到Rental class 去。为了这么做,我要运用Move Method。首先把代码拷贝到Rental class 内, 调整代码使之适应新家,然后重新编译。像下面这样。

 

class Rental...
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;
}

 在这个例子里,「适应新家」意味去掉参数。此外,我还要在搬移的同时变更函数名称。

 

现在我可以测试新函数是否正常工作。只要改变Customer.amountFor() 函数内容,使它委托(delegate)新函数即可。

 

class Customer...
private double amountFor(Rental aRental) {
    return aRental.getCharge();
}

 好了下面是三个类的最终代码

 

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;
    }

}

 

 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;
    }

    /**
     * 通计清单
     * 
     * @return
     */
    public String statement() {
        double totalAmount = 0;// 合计
        int frequentRentePoints = 0;
        Enumeration<Rental> enu_rentals = rentals.elements();
        String result = "Rental Record for " + this.getName() + " \n";
        while (enu_rentals.hasMoreElements()) {
            double thisAmount = 0;
            Rental each = enu_rentals.nextElement();

            thisAmount = each.getCharge();// 计算一笔租片费

            frequentRentePoints++;

            if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) {
                frequentRentePoints++;
            }

            result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(thisAmount) + "\n";

            totalAmount += thisAmount;

        }
        result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
        result += "You earned " + String.valueOf(frequentRentePoints) + " frequent renter points";
        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;
    }

}

 现在我可以编译并测试,看看有没有破坏了什么东西。

 

做完这些修改之后(图1.3),下一件事就是去掉旧函数。编译器会告诉我是否我漏掉了什么。然后我进行测试,看看有没有破坏什么东西。

 

本文章来自:http://blog.mkfree.com/posts/21

  • 大小: 16.3 KB
分享到:
评论

相关推荐

    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 为何重构 ...

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

    第1章 重构,第一个案例 1.1 起点 1.2 重构的第一步 1.3 分解并重组Statemen 1.4 运用多态取代与价格相关的条件逻辑 1.5 结语 第2章 重构原则 2.1 何谓重构 2.2 为何重构 2.3 何时重构 2.4 怎么对经理说 2.5 重构的...

    重构:改善既有代码的设计(中文高清版)

    第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 为何重构55 2.3 何时重构57 2.4 怎么...

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

    第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 为何重构55 2.3 何时重构57 2.4 怎么...

    程序员该读的十本好书之《重构改善既有代码的设计》

    木书是·木重构指南( guide to refacaoring ),为专业程序员而写。我的目的是告诉你如何以一种可控制且高效率的方式进行重构。你将学会这样的重构方式:不...我把最后一章〔第15章)留给重构技术的顶尖大师,Kent Beck.

    重构改善既有代码的设计

    这是java15本必读读物的第三本书,重构----改善既有代码的设计,扫描版,找了很久呢,至于第一本编程思想太大了没法上传,第二本agile java只有前四章,喜欢的还是买实物吧

    JAVA程序开发大全---上半部分

    3.6 MyEclipse中的Java代码重构支持 59 3.6.1 Java元素重命名 59 3.6.2 Java元素移动 60 3.6.3 Java元素复制 61 3.7 本章小结 61 第4章 Java开发的版本控制及SVN 62 4.1 版本控制与SVN 62 4.2 SVN服务器的安装与配置...

    Java版水果管理系统源码-RefactorMindMap:重构第一版重构手法读书笔记思维导图

    第一版以OOP为主 针对书本的核心部分6-11章做了思维导图可视化。 突然发现第二版上个月上市,直接下单了精装版,把这个坑填完我就自己去读第二版了哈哈 Link 第二版安利 为什么决定再版《重构》? 虽然这本书已经...

    java8stream源码-java8__:在Java中练习“现代Java实战”示例的代码

    和函数式编程一书中示例和测验的所有源代码。 您可以在这里购买这本书:或在亚马逊上 所有示例的源代码都可以在目录中找到 第 1 章:Java 8:你为什么要关心? 第 2 章:通过行为参数化传递代码 第 3 章:Lambda ...

    java8stream源码-Java8InAction:Java8上的示例代码和项目

    和函数式编程一书中示例和测验的所有源代码。 您可以在这里购买这本书:或在亚马逊上 所有示例的源代码都可以在目录中找到 第 1 章:Java 8:你为什么要关心? 第 2 章:通过行为参数化传递代码 第 3 章:Lambda ...

    java8stream源码-java8_in_action:“java8inaction”研究的示例代码

    和函数式编程一书中示例和测验的所有源代码。 您可以在这里购买这本书:或在亚马逊上 所有示例的源代码都可以在目录中找到 第 1 章:Java 8:你为什么要关心? 第 2 章:通过行为参数化传递代码 第 3 章:Lambda ...

    java8stream源码-java8-in-action:java8-in-action

    和函数式编程一书中示例和测验的所有源代码。 您可以在这里购买这本书:或在亚马逊上 所有示例的源代码都可以在目录中找到 第 1 章:Java 8:你为什么要关心? 第 2 章:通过行为参数化传递代码 第 3 章:Lambda ...

    java8stream源码-java8:Java8——函数式编程

    同时自己也做了一些扩展使用,修复了源码中的bug。 例如 Java 8 In Action 中的源代码可以在目录中找到: 第 1 章 Java 8:你为什么要关心? 第 2 章 使用行为参数化传递代码 第 3 章 Lambda 表达式 第 4 章 介绍流 ...

    java8stream源码-Java8:Java8

    和函数式编程一书中示例和测验的所有源代码。 您可以在这里购买这本书:或在亚马逊上 所有示例的源代码都可以在目录中找到 第 1 章:Java 8:你为什么要关心? 第 2 章:通过行为参数化传递代码 第 3 章:Lambda ...

    java8stream源码-git-flow-practise:git-flow-练习

    和函数式编程一书中示例和测验的所有源代码。 您可以在这里购买这本书:或在亚马逊上 所有示例的源代码都可以在目录中找到 第 1 章:Java 8:你为什么要关心? 第 2 章:通过行为参数化传递代码 第 3 章:Lambda ...

    【数据库】《数据库重构》.part1(1/5)

     本书前第5章介绍了演进式数据库开发的基本思想和技术,后6章详细描述了每一类重构,包括结构、数据质量、参照完整性、架构、方法的重构;另外还描述了不属于重构范畴的转换技术。  书中的示例代码是用Java、...

    java8stream源码-JavaTraining:Java培训

    和函数式编程一书中示例和测验的所有源代码。 您可以在这里购买这本书:或在亚马逊上 所有示例的源代码都可以在目录中找到 第 1 章:Java 8:你为什么要关心? 第 2 章:通过行为参数化传递代码 第 3 章:Lambda ...

    【数据库】《数据库重构》.part5(5/5)

     本书前第5章介绍了演进式数据库开发的基本思想和技术,后6章详细描述了每一类重构,包括结构、数据质量、参照完整性、架构、方法的重构;另外还描述了不属于重构范畴的转换技术。  书中的示例代码是用Java、...

Global site tag (gtag.js) - Google Analytics