zoukankan      html  css  js  c++  java
  • 重构,第一个案例

    一、租赁程序

    一个简单的影片租赁程序,计算每一个位顾客的消费金额并打印详单。

    顾客租了哪些影片、租期多长,程序便更具租赁时间和影片类型算出费用。

    影片分为三类:普通片、儿童片和新片。

    除了计算费用,还要为常客计算积分,积分会根据租片种类是否为新片而不同

    程序内容:

    movie类:

    /* movie.h */
    #ifndef MOVIE_H
    #define MOVIE_H
    
    #include <iostream>
    #include <string>
    
    class Movie
    {
    public:
        Movie(std::string title = "empty", int price = 0);
    
        int getPriceCode();
        void setPriceCode(int arg);
        std::string getTitle();
    
        static const int REGULAR;          //普通影片
        static const int NEW_RELEASE;      //新片
        static const int CHILDRENS;        //儿童影片
    private:
        std::string _title;                 //影片名
        int _priceCode;                     //价格码
    };
    
    
    #endif // MOVIE_H
    
    /* movie.cpp */
    #include "movie.h"
    
    const int REGULAR = 0;          //普通影片
    const int NEW_RELEASE = 1;      //新片
    const int CHILDRENS = 2;        //儿童影片
    
    Movie::Movie(std::__cxx11::string title, int price)
        :_title(title), _priceCode(price)
    {
    
    }
    
    int Movie::getPriceCode()
    {
        return _priceCode;
    }
    
    void Movie::setPriceCode(int arg)
    {
        _priceCode = arg;
    }
    
    std::__cxx11::string Movie::getTitle()
    {
        return _title;
    }
    movie类

    rental类:

    /* rental.h */
    #ifndef RENTAL_H
    #define RENTAL_H
    
    #include "movie.h"
    
    class Rental
    {
    public:
        Rental(Movie movie, int daysRented);
        int getDaysRented();
        Movie getMovie();
    
    private:
        Movie _movie;       //租赁的影片
        int _daysRented;    //租期
    };
    
    #endif // RENTAL_H
    
    /* rental.cpp */
    #include "rental.h"
    
    Rental::Rental(Movie movie, int daysRented)
    {
        _movie = movie;
        _daysRented = daysRented;
    }
    
    int Rental::getDaysRented()
    {
        return _daysRented;
    }
    
    Movie Rental::getMovie()
    {
        return _movie;
    }
    Rental类

    customer类:

    /* customer.h */
    #ifndef CUSTOMER_H
    #define CUSTOMER_H
    
    #include <string>
    #include <iostream>
    #include <vector>
    #include "movie.h"
    #include "rental.h"
    
    class Customer
    {
    public:
        Customer(std::string name);
        void addRental(Rental arg);
        std::string getName();
        std::string statement();
        std::vector<Rental>& getRentals();
    
    private:
        std::string _name;                  //顾客名
        std::vector<Rental> _rentals;       //租赁列表
    };
    
    #endif // CUSTOMER_H
    
    /* customer.cpp */
    #include "customer.h"
    
    Customer::Customer(std::__cxx11::string name)
    {
        _name = name;
    }
    
    void Customer::addRental(Rental arg)
    {
        _rentals.push_back(arg);
    }
    
    std::__cxx11::string Customer::getName()
    {
        return _name;
    }
    
    std::__cxx11::string Customer::statement()
    {
        double totalAmount = 0;                 //总金额
        int frequentRenterPoints = 0;           //积分点
        
        std::string result = "Rental Record for " + getName() + "
    ";
        std::vector<Rental>::iterator iter = _rentals.begin();
        for(;iter != _rentals.end();++iter) {
            double thisAmount = 0;              //当前单个租赁金额
            Rental each = *iter;
    
            switch(each.getMovie().getPriceCode()) {
            case 0:             //普通片,起步价为2元,租期超过2天的部分每天1.5元
                thisAmount += 2;
                if(each.getDaysRented() > 2)
                    thisAmount += (each.getDaysRented() - 2) * 1.5;
                break;
            case 1:             //新片,每天3元
                thisAmount += each.getDaysRented() * 3;
                break;
            case 2:             //儿童片,起步价1.5元,租期超过3天的部分每天1.5元
                thisAmount += 1.5;
                if(each.getDaysRented() > 3)
                    thisAmount += (each.getDaysRented() - 3) * 1.5;
                break;
            }
            frequentRenterPoints++;         //每借一张加1个积分点
            //积分累加条件:新版本的片子,借的时间大于1天
            if((each.getMovie().getPriceCode() == 1) && each.getDaysRented() > 1) {
                frequentRenterPoints++;
            }
            //添加详单
            result += "	" + each.getMovie().getTitle() + "	"
                    + std::to_string(thisAmount) + "
    ";
            totalAmount += thisAmount;
        }
        //添加脚注
        result += "Amount owed is " + std::to_string(totalAmount) + "
    ";
        result += "You earned " + std::to_string(frequentRenterPoints) +
                " frequent renter points" +"
    ";
        return result;
    }
    
    std::vector<Rental> &Customer::getRentals()
    {
        return _rentals;
    }
    Customer类

    main程序:

    #include <iostream>
    #include <string>
    #include <vector>
    
    #include "movie.h"
    #include "rental.h"
    #include "customer.h"
    
    using namespace std;
    
    int main()
    {
        /* create 10 movies */
        std::vector<Movie> movies;
        for(int i=0;i<10;i++) {
            Movie tempMovie("Movie"+std::to_string(i+1), i+1);
            movies.push_back(tempMovie);
        }
    
        /* create 5 customers */
        std::vector<Customer> customers;
        for(int i=0;i<5;i++) {
            Customer tempCustomers("customer" + std::to_string(i+1));
            for(int j=2*i;j<2*i+2;++j) {
                Movie tempMovie = movies[j];
                Rental tempRent(tempMovie, i+1);
                tempCustomers.addRental(tempRent);
            }
    
            customers.push_back(tempCustomers);
        }
    
        //print out all movies information;
        const std::vector<Movie>::size_type numMovies = movies.size();
        for(int i=0;i<numMovies;++i) {
            Movie tempMovie = movies[i];
            std::cout << " the Tile of the "<<i+1 << "("
                     << tempMovie.getTitle() << "," << tempMovie.getPriceCode() << ")"
                     << std::endl;
        }
        std::cout << std::endl;
    
        //print out all customers information
        const std::vector<Customer>::size_type numCustomers = customers.size();
        for(int i=0;i<numCustomers;++i) {
            Customer tempCust = customers[i];
            std::cout << "the " << std::to_string(i+1) << " the customer " << tempCust.getName()
                      << " has rented these movies:" << std::endl;
            const std::vector<Rental>::size_type numRentals = tempCust.getRentals().size();
            for(int j=0;j<numRentals;++j) {
                std::cout << "    (" << tempCust.getRentals()[j].getMovie().getTitle()
                          << ", " << tempCust.getRentals()[j].getDaysRented() << ")" << std::endl;
            }
        }
        std::cout << std::endl;
    
        for(int i=0;i<numCustomers;++i) {
            Customer tempCust = customers[i];
            std::cout << tempCust.statement() << std::endl;
        }
    
        return 0;
    }
    main程序

    1.2 程序评价

    customer里头的statement()做的事情太多了,它做了很多应该其他类做的事情。

    如果需要修改输出的格式,那就需要再增加一个新的计算函数。

    如果需要修改影片的分类方式,它又会影响顾客消费和常客积分。这样程序又需要更改了。

    如果发现自己需要为程序添加一个特性,代码结构让你无法很方便地达成目的,就先重构那个程序,让代码更容易添加特性。

    重构前,先检查自己是否有一套可靠的测试机制。这些测试必须可以自我检验。

    1.3 分解和重组statement()

    第一步:找出代码的逻辑泥团并运用Extract Method

    然后:找出函数内的局部变量和参数

    其次:找出其中的被修改的和未被修改的。

    未被修改的:用作参数

    修改的:用作返回值

    把switch提取出来作为函数

    重构技术就是以微小步伐修改程序,哪怕出了错误,也能方便的修改

    提炼常客积分代码

    去除thisAmount临时变量

    去除totalAmount临时变量

     

    1.4 运用多态取代与加个相关的条件逻辑

    switch语句,最好不要在另一个对象的属性基础上运用switch语句。

    哪怕不得不使用,也应该在对象自己的数据上使用。而不是在别人的数据上使用。

    将getCharge()移动到Movie类中去

    同样的方法修改常客积分

    继承方式提供可修改的分类

    步骤一:针对类型代码使用Self Encapsulate Field,确保仍和时候都通过取值函数和设值函数来访问类型代码。

    多态中用到的变量用,取值和设值函数替代

    新建Price类,提供相关行文,加上子类的对应具体函数

    Movie类不再保存_priceCode变量,而用Price对象(state模式)

    新建Price类,提供相关行文,加上子类的对应具体函数

    同样的手法处理getFrequentRenterPoints,但是出错了 

    1.5 在编写代码时遇到的问题

    最后修改getFrequentRenterPoints,多态没有很好的执行。

    最后发现CPP基类函数如果没有加virtual是不会重载的,子类依然会执行父类的函数,而不是自己的同名函数。

    这一点连编译器都没有提示。代码修改链接

  • 相关阅读:
    in_array函数的第三个参数 strict
    主动创建缓存与被动创建缓存
    INSERT IGNORE 与INSERT INTO的区别
    说说php取余运算(%)的那点事
    继承中的类常量的使用
    Scribe 分布式日志收集系统
    PHP命令行模式基本介绍
    【C/C++】学生排队吃饭问题
    【C/C++】链表
    【C/C++】01背包问题/动态规划
  • 原文地址:https://www.cnblogs.com/ch122633/p/10474038.html
Copyright © 2011-2022 走看看