1. 程式人生 > >重構,第一個案例

重構,第一個案例

第一步 bee endif 無法 分享 ace 消費 pre 金額

一、租賃程序

一個簡單的影片租賃程序,計算每一個位顧客的消費金額並打印詳單。

顧客租了哪些影片、租期多長,程序便更具租賃時間和影片類型算出費用。

影片分為三類:普通片、兒童片和新片。

除了計算費用,還要為常客計算積分,積分會根據租片種類是否為新片而不同

程序內容:

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() + "\n";
    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 += "\t" + each.getMovie().getTitle() + "\t"
                + std::to_string(thisAmount) + "\n";
        totalAmount += thisAmount;
    }
    //添加腳註
    result += "Amount owed is " + std::to_string(totalAmount) + "\n";
    result += "You earned " + std::to_string(frequentRenterPoints) +
            " frequent renter points" +"\n";
    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

然後:找出函數內的局部變量和參數

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

未被修改的:用作參數

修改的:用作返回值

重構,第一個案例