重构-改善既有代码的设计--重构,第一个案例

什么是重构

在不改变代码外在行为的前提下,对代码做出修改以改进程序内部的结构
简单地说就是在代码写好后改进它的设计

谁该阅读这本书

  1. 专业程序员(能够提高你的代码质量)
  2. 资深设计师和架构规划师(理解为什么需要重构,哪里需要重构)

阅读技巧

带着疑问去读:

  • 如果你想要知道重构是什么。第1章够了
  • 如果你想要知道为什么要重构,第1,2章
  • 如果你想知道该在什么地方重构,第3章
  • 如果你想进行重构,第1,2,3,4章。并根据目录选读

第1章 重构,第一个案例

public String statement(){

        double totalAmount=0;
        int frequentRenterPoints=0;
        Enumeration<Rental> rentals = _rentals.elements();

        String result = "Rental Record for "+getName()+"\n";
        while(rentals.hasMoreElements()){

            double thisAmount=0;
            Rental each = (Rental)rentals.nextElement();

            switch (each.getMovie().getPriceCode()) {
            case Movie.CHILDRENS:

                thisAmount += 1.5;
                if(each.getDaysRented()>3){
                    thisAmount += (each.getDaysRented()-3)*1.5;
                }
                break;
            case Movie.NEW_RELEASE:

                thisAmount += each.getDaysRented()*3;
                break;
            case Movie.REGULAR:

                thisAmount += 2;
                if(each.getDaysRented()>2){
                    thisAmount += (each.getDaysRented()-2)*1.5;
                }
                break;

            default:
                break;
            }

            frequentRenterPoints++;

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

            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(frequentRenterPoints) + " frequent renter points ";

        return result;
    }

这是只是一个方法。直接评价:太复杂,复用率低

解决方法

分解并重组

将代码按照功能拆分。每个功能只做一件事。

拆除switch并封装为方法
private double amountFor(Rental each, double result) {
    switch (each.getMovie().getPriceCode()) {
    case Movie.CHILDRENS:

        result += 1.5;
        if(each.getDaysRented()>3){
            result += (each.getDaysRented()-3)*1.5;
        }
        break;
    case Movie.NEW_RELEASE:

        result += each.getDaysRented()*3;
        break;
    case Movie.REGULAR:

        result += 2;
        if(each.getDaysRented()>2){
            result += (each.getDaysRented()-2)*1.5;
        }
        break;

    default:
        break;
    }
    return result;
}



重设变量名

变量名必须保证简单清楚,不产生歧义。比如上方代码的each就是可修改的变量名。因为each到底指的是什么

搬移代码

因为这个方法只使用了rental的信息。所以需要放在rental类下

去除临时变量

statement方法中有两个临时变量totalAmount和frequentRenterPoints。 做成getTotalAmount和getFrequentRenterPoints方法

public String statement() {
        double totalAmount = 0;
        int frequentRenterPoints = 0;
        // Enumeration接口定义了从一个数据结构得到连续数据的手段
        Enumeration rentals = _rentals.elements();

        String result = "Rental Record for" + getName() + "\n";
        while (rentals.hasMoreElements()) {
            Rental each = (Rental) rentals.nextElement();

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


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

        return result;

    }
private int getTotalFrequentRenterPoints(){
    int result=0;
    Enumeration rentals=_rentals.elements();
    while(rentals.hasMoreElements()){
        Rental each=(Rental)rentals.nextElement();
        result+=each.getMovie().getFrequentRenterPoints(each.getDayRented());
    }
    return result;
}
private double getTotalCharge(){
    double result =0;
    Enumeration rentals=_rentals.elements();
    while(rentals.hasMoreElements()){
        Rental each=(Rental)rentals.nextElement();
        result+=each.getMovie().getPrice().getCharge(each.getDayRented());
    }
    return result;
}
测试重构后的方法

保证重构后的方法能够满足所有的需求

总结
  • 代码块俞小,代码的功能就俞容易管理,代码的处理和移动也就俞轻松
  • 任何不会被修改的变量都可以被当成参数传入新的函数,至于会被修改的变量需要慎重。如果只有一个变量会被修改,可以把它当做返回值。
  • 绝大多数情况下,函数应该放在它所使用的数据的所属对象内
  • 最好不要在另一个对象的属性基础上运用switch语句。如果不得不使用,也应该在对象自己的数据上使用,而不是在别人的数据上使用。
  • 使用继承来适当组织类关系后,可以用多态取代switch语句。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * 原始需求背景: * 网宿CDN要按月收取客户的服务费用,根据流量的大小、 * 服务的类型等,收取不同的费用,收费规则如下: * web应用:1000元/M * 流媒体应用:1000元/M*0.7 * 下载应用:1000元/M*0.5 * 月末打印报表时,要罗列每个用户每个频道的费用、客户总费用, * 还要打印该客户的重要性指数,重要性指数=网页流/100+下载流量/600; * * 需求变更场景: * 系统已经开发出来了,接下来,运维部门现在希望对系统做一点修改, * 首先,他们希望能够输出xml,这样可以被其它系统读取和处理,但是, * 这段代码根本不可能在输出xml的代码中复用report()的任何行为,唯一 * 可以做的就是重写一个xmlReport(),大量重复report()中的行为,当然, * 现在这个修改还不费劲,拷贝一份report()直接修改就是了。 * 不久,成本中心又要求修改计费规则,于是我们必须同时修改xmlReport() * 和report(),并确保其一致性,当后续还要修改的时候,复制-黏贴的问题就 * 浮现出来了,这造成了潜在的威胁。 * 再后来,客服部门希望修改服务类型和用户重要性指数的计算规则, * 但还没决定怎么改,他们设想了几种方案,这些方案会影响用户的计费规则, * 程序必须再次同时修改xmlReport()和report(),随着各种规则变得越来越复杂, * 适当的修改点越 来越难找,不犯错误的机会越来越少。 * 现在,我们运用所学的OO原则和方法开始进行改写吧。 */

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值