`
381895649
  • 浏览: 230332 次
  • 性别: 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
分享到:
评论

相关推荐

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

    第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 重构的...

    软件工程中的代码重构技术.pptx

    #### 第2章:代码重构的方法 **2.1 重构的基本概念** - **提炼函数**:将函数的一部分代码抽取出来,形成一个新的独立函数。 - **搬移字段**:将一个类中的字段移动到另一个类中。 - **封装类**:为类中的字段和...

    代码大全第二版 中文版(1-20章)

    - 第一章“代码质量”讨论了代码质量的重要性,包括可读性、可维护性和可扩展性。 - 第二章“规划重构”讲述了如何在项目早期进行代码结构的规划,以及如何通过重构来改进代码。 - 第三章“设计原则”介绍了 ...

    Java第9章 管理类文件含源代码

    总结来说,"Java第9章 管理类文件含源代码"这一主题涵盖了Java类加载机制、类路径配置、源代码管理和调试等多个方面。通过研究提供的源代码,开发者可以更好地理解和实践这些概念,从而在实际项目中更有效地管理类...

    软件工程中的代码重构与性能优化.pptx

    #### 第一章:简介 **代码重构与性能优化概述** - **代码重构**:是指在不改变软件外部行为的前提下,对内部结构进行调整,以提高代码质量的过程。这通常涉及到对代码结构的改善、减少冗余、提高可读性和可维护性...

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

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

    Java项目开发代码Review常见问题实例.doc

    **第一章 综述** 代码审查是软件开发流程中的一个环节,它涉及同行对代码进行检查,以找出潜在的缺陷、不一致性和优化空间。在Java项目中,这通常包括对命名规范、代码格式、性能影响、系统稳定性和编程错误等方面...

    使用Xtext和Xtend实现域特定语言(第二版)-中文翻译-第一章

    本章介绍的内容还包括如何安装和使用Xtext框架,以及在Eclipse中创建第一个DSL示例。Xtext利用了Eclipse平台强大的插件和扩展机制,提供了对DSL的全方位支持,包括语法定义、语法高亮、代码补全、错误检查、代码重构...

    java代码规范

    Java代码规范是软件开发过程中不可或缺的一部分,它不仅有助于提高代码的可读性和可维护性,还能促进团队协作,确保项目的一致性和质量。以下是对给定文件中的几个关键章节进行的详细解读,涵盖了一般规则、格式规范...

    重构改善既有代码的设计

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

    java测试源码-tdd-java-ch05-design-my-version:Java测试驱动开发第5章

    【Java测试驱动开发第5章】是关于使用TDD(测试驱动开发)方法在Java环境中进行软件设计的一个章节。TDD是一种敏捷开发实践,强调先编写测试,再编写实现代码,以此来确保代码质量并降低维护成本。在这个章节中,...

    白痴都能看懂的Java教程(第二章:Eclipse的安装使用以及人生第一个Java项目实战)

    6. 在生成的类文件中,编写第一个Java程序的标准输出语句:`System.out.println("Hello, World!");`。 7. 运行程序,通过点击工具栏中的运行按钮或右键选择“Run As”,选择“Java Application”来运行程序。 8. ...

    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服务器的安装与配置...

    MyEclipse.6.Java.开发中文教程(1-10章).pdf

    第一章:MyEclipse简介 这一章主要介绍了MyEclipse的基本概念,它是一款强大的Java集成开发环境(IDE),支持多种开源技术,如Java、J2EE、Web、AJAX等。此外,还讨论了MyEclipse相对于其他IDE的优势,以及如何下载...

    数据结构:java语言 第一章

    本课程“数据结构:Java语言”第一章主要探讨了数据结构和对象的基础,并引入了一个关键概念——预条件(preconditions)和后条件(postconditions),这对于理解和编写高质量的代码至关重要。 预条件和后条件是...

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

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

    编写Java个人档案

    在第一章“初识Java”中,我们将更深入地学习Java的基本语法,包括变量、运算符、流程控制语句(如if、for、while)以及数组。此外,我们还会探索Eclipse的更多功能,如代码自动完成、重构和错误检查,以提高开发...

    精通Eclipse(第二版9-23章)光盘源代码

    《精通Eclipse(第二版9-23章)光盘源代码》涵盖了Eclipse IDE的高级使用技巧和开发实践,是深入理解并掌握Eclipse这一强大开发工具的重要资源。源代码包含了从第9章到第23章的全部示例项目,旨在帮助读者通过实际操作...

Global site tag (gtag.js) - Google Analytics