重构简单案例——《重构——改善既有代码的设计》

1. 前言重构,可以理解为一种帮助你改进已有代码设计的一种方法。若直接对这种方法下一个定义,那么很容易陷入形式,读完后还是不理解重构是啥。因为它是在你不断设计,不断改进过程中归纳出来的一些比较通用的手法,这些手法会因你遇到的场景及上下文的不同而发生变化,而代码设计过程中的场景是千变万化,异常复杂的。所以,以一个简单案例入手,帮助我们熟悉一般简单场景重构的手法,然后一步步组合变化,去满足我们对大型...
摘要由CSDN通过智能技术生成

1. 前言

重构,可以理解为一种帮助你改进已有代码设计的一种方法。若直接对这种方法下一个定义,那么很容易陷入形式,读完后还是不理解重构是啥。因为它是在你不断设计,不断改进过程中归纳出来的一些比较通用的手法,这些手法会因你遇到的场景及上下文的不同而发生变化,而代码设计过程中的场景是千变万化,异常复杂的。所以,以一个简单案例入手,帮助我们熟悉一般简单场景重构的手法,然后一步步组合变化,去满足我们对大型系统重构的需求是一个很好的思路。

2. 初始需求的描述

该程序用于影片出租店,出租店目前有三种类型的影片出租:普通片、儿童片、新片。需要记录顾客租了哪些影片,租期,并计算租金,统计顾客积分。

2.1 第一版设计

设计三个类:Movie, Rental, Consumer,其中顾客可以查看自己的租金以及积分,在Consumer类增加一个statement接口,用于打印该顾客的租用详单,其设计类图如下:
第一版设计类图
图一 第一版设计类图

设计代码如下:

#include <iostream>
using namespace std;

enum MovieType {
    CHILDRENS,         //儿童片
    REGULAR,           // 普通片
    NEW_RELEASE,       //新片
}

class Movie
{
public:
    MovieType GetMovieType() {
        return type;
    }
    
    string GetMovieTitle() {
        return name;
    }
    
private:
    string name;
    double price;
    MovieType type;
    
};

class Rental
{
public:
    Movie GetMovie() {
        return mv;
    }
    
    int GetRentDays() {
        return days;
    }
private:
    int days;
    Movie mv;
};

class Consumer
{
public:
    string statement();
    string GetName() {
        return name;
    }
    
private:
    string name;
    vector<Rental> rentals;
    
    
};

// 打印详单,包含计算总价格及总积分
string Consumer::statement() {
    double totalAmount = 0;
    int frequentRenterPoints = 0;
    Enumeration rentals = _rentals.elements();
    string result = "Rental Record for " + GetName() + "\n";
    int i = 0; 
    
    while (i < rentals.size()) {
        double thisAmount = 0;

        //determine amounts for each line
        switch (rentals[i].GetMovie().GetMovieType()) {
            case REGULAR:
                {
                    thisAmount += 2;
                    if (rentals[i].GetRentDays() > 2) {
                        thisAmount += (rentals[i].GetRentDays()- 2)* 1.5;
                    }
                }
                break;
            case NEW_RELEASE:
                {
                    thisAmount += rentals[i].GetRentDays()* 3;
                }
                break;
            case CHILDRENS:
                {
                    thisAmount += 1.5;
                    if (rentals[i].GetRentDays() > 3) {
                        thisAmount += (rentals[i].GetRentDays()- 3)* 1.5;
                    }
                }
                break;
            default:
                break;
        }
        // add frequent renter points
        frequentRenterPoints ++;
        // add bonus for a two day new release rental
        if ((rentals[i].GetMovie().GetMovieType()== Movie.NEW_RELEASE) &&
        rentals[i].GetRentDays() > 1) {
            frequentRenterPoints ++;
        }
        //show figures for this rental
        result += "\t" + rentals[i].GetMovie().GetMovieTitle()+ "\t" +
        to_string(thisAmount)+ "\n";
        totalAmount += thisAmount;
        i++;
    }
    //add footer lines
    result += "Amount owed is " + to_string(totalAmount)+ "\n";
    result += "You earned " + to_string(frequentRenterPoints)+
    " frequent renter points"
    return result;
}

int main() {
	cout  << "test\n";
	return 0;
}

分析以上代码,发现可以实现计算租金及积分,并打印出详单的基本功能,但明显存在很多不足:

  1. statement函数太长,里面做的工作太多
  2. 若需要以HTML形式打印表单,那么statement接口无法复用
  3. 可以复制代码重新增加htmlStatement接口,但若计费标准发生变化,需要同时修改两个接口
  4. 若影片种类增加或计费规则改变,那么需要不断修改两个接口,无法确保两处修改一致
  5. 难以满足不断变化的客户需求

2.2 第二版设计

首先,针对statement函数过长,功能点集中的缺点进行改善;

第一步: 识别statement函数中较小的代码块,将其提取成一个函数分离出来。经过观察发现,可以将计算每一次租赁的价格的代码块提取出来,不变的租赁实例作为入参,唯一可

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值