1. 程式人生 > >重構-改善既有代碼的設計 讀書筆記一

重構-改善既有代碼的設計 讀書筆記一

java 方法 變化 show points col total 。。 -a

影片出租店的程序:計算每位顧客的消費金額並打印詳單。
操作者告訴程序:顧客租了哪些影片,租期多長。程序根據租賃時間和影片類型計算費用。
影片分為3類:普通片,兒童片,新片。除了計算費用還要為常客計算積分,積分根據組片種類是否為新片而不同。

Movie(movieKind, name)
Rent(影片名稱,租賃時間)
RentTotal(Rent列表)

階段1:從statement()到htmlStatement()
使用的重構方法:
1.extract method(抽取邏輯,可能在多處使用的相同邏輯,只需要保留一份邏輯代碼,而在使用時對該邏輯進行調用。
而不是寫重復的邏輯代碼。)

確保邏輯代碼唯一,而不是邏輯代碼重復。
即確保邏輯實體唯一,而不是邏輯實體重復。
確保只有一個該邏輯的實體,而不是多個該邏輯的實體。因為如果某個邏輯產生多個實體,必須維護它們的一致性。
唯一的邏輯實體 + 邏輯實體的多次調用。
原因:邏輯代碼重復,當需要修改該邏輯代碼時,必須確保該邏輯代碼的所有實體都是一致性。
這個一致性維護工作難度根據邏輯實體分散程度增加,邏輯實體的個數增加,出錯的概率也不斷提升。

2.move method(函數搬家,函數應該放在它使用的數據的對象內)
調整修改代碼,讓其使用新函數,舊函數如果是public則可以保留,如果其他類還引用了舊函數,不需要修改它們。
3.replace temp with query。(使用函數而不是臨時變量)

階段2:結合變化量,影片的類型,限制變化的影響範圍。本例中將影片類型的變化影響限制到Movie類中。
如果getCharge()和getRentPoints()方法在Movie類中,我們只需要在Movie類中修改類型,getCharge() getRentPoints()方法。
如果getCharge()和getRentPoints()方法在Rental類中,當影片類型發生變化,我們需要在Movie類中需改類型,並在Rental類中修改這2個方法。
盡管修改的東西都是一樣的,但是後者在2個類中修改,前者在1個類中修改。顯然前者更好。
所以把Rental中的getCharge()方法和getRentPoints()方法搬到Movie類中
方式:將Rental中的getCharge()方法和getRentPoints()方法搬到Movie類中

階段3:使用多態替換類型代碼
1.replace type code with state/strategy
1.self encapsulate field
2.move method
3.replace conditional with polymorphism

重構的節奏:測試小修改,測試小修改。。。

重構(名詞):對軟件內部結構的一種調整,目的是在不改變軟件可觀察行為的前提下,提高其可理解性,降低其修改成本。
重構(動詞):使用一系列重構手法,在不改變軟件可觀察行為的前提下,調整其結構。
重構是對軟件的小修改,但重構之中還可以包含另一個重構。

重構的目的:在不改變軟件可觀察行為的前提下,調整內部結構,使其更容易被理解和修改。
性能優化:在不改變軟件可觀察行為的前提下,調整內部結構,提高性能,但往往代碼會變得更難理解。

使用重構技術開發軟件:
時間分配給2種截然不同的行為:添加新功能,以及重構。
添加新功能時不應修改代碼,只管添加功能。
重構時不應添加新功能,只管調整結構。
經常變換帽子,但無論何時都應該清楚自己戴的是哪頂帽子。

重構把我帶到更高的理解層次。

重構使程序擁有良好的設計,而良好的設計使快速開發的根本。
如果沒有良好的設計,某段時間你可能進展迅速,但惡劣的設計很快就讓你的速度慢下來。你會花時間在調試上面,修改的時間
越來越長,因為你必須花更多的時間取理解系統、尋找重復的代碼,隨著你給程序打上一個有一個補丁,新特性需要更多的代碼來實現。
真是個惡性循環。

何時重構?
重構本來就不是一件應該特別撥出時間做的事情,隨時隨地都可以。
如果你想做某件事情,而重構可以幫你把它做得更好,就用重構。
如果你想理解代碼,而重構可以幫助你理解,那就重構。
如果你想添加新功能,重構可以幫助你理解代碼,並更容易的添加新功能。
技術分享圖片
 1 package shop;
 2 
 3 public class Movie {
 4     public static final int NEW_RELEASE = 0;
 5     public static final int REGULAR = 1;
 6     public static final int CHILDREN = 2;
 7 
 8 //    private int priceCode;
 9     private String title;
10     private Price price;
11 
12     public Movie() {
13     }
14 
15     public Movie(String title, int priceCode) {
16         setPriceCode(priceCode);
17         this.title = title;
18     }
19 
20     public int getPriceCode() {
21         return price.getPriceCode();
22     }
23 
24     public void setPriceCode(int priceCode) {
25         switch (priceCode){
26             case Movie.NEW_RELEASE:
27                 price = new NewPrice();
28                 break;
29             case Movie.REGULAR:
30                 price = new RegularPrice();
31                 break;
32             case Movie.CHILDREN:
33                 price = new ChildrenPrice();
34                 break;
35             default:
36                 throw new IllegalArgumentException("非法的影片類型:" + priceCode);
37         }
38     }
39 
40     public String getTitle() {
41         return title;
42     }
43 
44     public void setTitle(String title) {
45         this.title = title;
46     }
47 
48     public double getCharge(int daysRent){
49         return price.getCharge(daysRent);
50 //        double result = 0;
51 //        switch(getPriceCode()){
52 //            case Movie.REGULAR:
53 //                result += 2;
54 //                if(daysRent > 2){
55 //                    result += (daysRent - 2) * 1.5;
56 //                }
57 //                break;
58 //            case Movie.NEW_RELEASE:
59 //                result += daysRent * 3;
60 //                break;
61 //            case Movie.CHILDREN:
62 //                result += 1.5;
63 //                if(daysRent > 3){
64 //                    result += (daysRent - 3) * 1.5;
65 //                }
66 //                break;
67 //        }
68 //        return result;
69     }
70 
71     public int getRentPoints(int daysRent){
72         return price.getRentPoints(daysRent);
73 //        int rentPoints = 1;
74 //        if(getPriceCode() == Movie.NEW_RELEASE
75 //                && daysRent > 1){
76 //            rentPoints++;
77 //        }
78 //        return rentPoints;
79     }
80 }
Movie 技術分享圖片
 1 package shop;
 2 
 3 public class Rental {
 4     private Movie movie;
 5     private int daysRent;
 6 
 7     public Rental(){}
 8 
 9     public Rental(Movie movie, int daysRent){
10         this.movie = movie;
11         this.daysRent = daysRent;
12     }
13 
14     public Movie getMovie() {
15         return movie;
16     }
17 
18     public void setMovie(Movie movie) {
19         this.movie = movie;
20     }
21 
22     public int getDaysRent() {
23         return daysRent;
24     }
25 
26     public void setDaysRent(int daysRent) {
27         this.daysRent = daysRent;
28     }
29 
30     public int getRentPoints(){
31         return getMovie().getRentPoints(getDaysRent());
32 //        int rentPoints = 1;
33 //        if(getMovie().getPriceCode() == Movie.NEW_RELEASE
34 //                && getDaysRent() > 1){
35 //            rentPoints++;
36 //        }
37 //        return rentPoints;
38     }
39 
40     public double getCharge(){
41         return getMovie().getCharge(getDaysRent());
42 //        double result = 0;
43 //        switch(this.getMovie().getPriceCode()){
44 //            case Movie.REGULAR:
45 //                result += 2;
46 //                if(this.getDaysRent() > 2){
47 //                    result += (this.getDaysRent() - 2) * 1.5;
48 //                }
49 //                break;
50 //            case Movie.NEW_RELEASE:
51 //                result += this.getDaysRent() * 3;
52 //                break;
53 //            case Movie.CHILDREN:
54 //                result += 1.5;
55 //                if(this.getDaysRent() > 3){
56 //                    result += (this.getDaysRent() - 3) * 1.5;
57 //                }
58 //                break;
59 //        }
60 //        return result;
61     }
62 }
Rental 技術分享圖片
 1 package shop;
 2 
 3 import java.util.ArrayList;
 4 
 5 public class CustomerOrder01 {
 6     private String name;
 7     private ArrayList<Rental> rentals = new ArrayList<>();
 8 
 9     public CustomerOrder01(String name){
10         this.name = name;
11     }
12 
13     public void addRental(Rental rental){
14         rentals.add(rental);
15     }
16 
17     public String statement(){
18         String result ="Rental Record for " + getName() + "\n";
19         for (Rental rental : rentals) {
20 //            double thisAmount = 0;
21 //            switch(rental.getMovie().getPriceCode()){
22 //                case Movie.REGULAR:
23 //                    thisAmount += 2;
24 //                    if(rental.getDaysRent() > 2){
25 //                        thisAmount += (rental.getDaysRent() - 2) * 1.5;
26 //                    }
27 //                    break;
28 //                case Movie.NEW_RELEASE:
29 //                    thisAmount += rental.getDaysRent() * 3;
30 //                case Movie.CHILDREN:
31 //                    thisAmount += 1.5;
32 //                    if(rental.getDaysRent() > 3){
33 //                        thisAmount += (rental.getDaysRent() - 3) * 1.5;
34 //                    }
35 //                    break;
36 //            }
37 //            thisAmount = amountFor(rental);
38 //            thisAmount = rental.getCharge();
39 //            rentPoints++;
40 //            if(rental.getMovie().getPriceCode() == Movie.NEW_RELEASE
41 //                && rental.getDaysRent() > 1){
42 //                rentPoints++;
43 //            }
44             result += "\t" + rental.getMovie().getTitle() + "\t" + String.valueOf(rental.getCharge()) + "\n";
45         }
46         result += "Amount owed is " + String.valueOf(getTotalAmount()) + "\n";
47         result += "You earned " + String.valueOf(getTotalRentPoints()) + " frequent renter points";
48         return result;
49     }
50 
51     public double amountFor(Rental rental){
52 //        double result = 0;
53 //        switch(rental.getMovie().getPriceCode()){
54 //            case Movie.REGULAR:
55 //                result += 2;
56 //                if(rental.getDaysRent() > 2){
57 //                    result += (rental.getDaysRent() - 2) * 1.5;
58 //                }
59 //                break;
60 //            case Movie.NEW_RELEASE:
61 //                result += rental.getDaysRent() * 3;
62 //                break;
63 //            case Movie.CHILDREN:
64 //                result += 1.5;
65 //                if(rental.getDaysRent() > 3){
66 //                    result += (rental.getDaysRent() - 3) * 1.5;
67 //                }
68 //                break;
69 //        }
70 //        return result;
71         return rental.getCharge();
72     }
73 
74     public double getTotalAmount(){
75         double totalAmount = 0;
76         for (Rental rental : rentals) {
77             totalAmount += rental.getCharge();
78         }
79         return totalAmount;
80     }
81 
82     public int getTotalRentPoints(){
83         int totalRentPoints = 0;
84         for (Rental rental : rentals) {
85             totalRentPoints += rental.getRentPoints();
86         }
87         return totalRentPoints;
88     }
89     public String getName(){
90         return name;
91     }
92 }
CustomerOrder 技術分享圖片
 1 package shop;
 2 
 3 public class CustomerTest01 {
 4     public static void main(String[] args) {
 5         Movie movie1 = new Movie("阿凡達", Movie.NEW_RELEASE);
 6         Movie movie2 = new Movie("僵屍世界大戰", Movie.REGULAR);
 7         Movie movie3 = new Movie("熔爐", Movie.CHILDREN);
 8         Movie movie4 = new Movie("星際穿越", Movie.REGULAR);
 9 
10         Rental rental1 = new Rental(movie1,3);
11         Rental rental2 = new Rental(movie2,5);
12         Rental rental3 = new Rental(movie3,5);
13 
14         CustomerOrder01 customerOrder = new CustomerOrder01("liubei");
15         customerOrder.addRental(rental1);
16         customerOrder.addRental(rental2);
17         customerOrder.addRental(rental3);
18 
19         System.out.println(customerOrder.statement());
20     }
21 }
CustomerTest 技術分享圖片
 1 package shop;
 2 
 3 public abstract class Price {
 4     public abstract int getPriceCode();
 5 
 6     public abstract double getCharge(int daysRent);
 7 
 8     public int getRentPoints(int daysRent){
 9         return 1;
10 //        int rentPoints = 1;
11 //        if(getPriceCode() == Movie.NEW_RELEASE
12 //                && daysRent > 1){
13 //            rentPoints++;
14 //        }
15 //        return rentPoints;
16     }
17 }
Price 技術分享圖片
 1 package shop;
 2 
 3 public class NewPrice extends Price{
 4     @Override
 5     public int getPriceCode() {
 6         return Movie.NEW_RELEASE;
 7     }
 8 
 9     @Override
10     public double getCharge(int daysRent) {
11         return daysRent * 3;
12     }
13 
14     @Override
15     public int getRentPoints(int daysRent) {
16         return daysRent > 1 ? 2 : 1;
17     }
18 }
NewPrice 技術分享圖片
 1 package shop;
 2 
 3 public class ChildrenPrice extends Price {
 4     @Override
 5     public int getPriceCode(){
 6         return Movie.CHILDREN;
 7     }
 8 
 9     @Override
10     public double getCharge(int daysRent) {
11         double result = 1.5;
12         if(daysRent > 3){
13             result += (daysRent - 3) * 1.5;
14         }
15         return result;
16     }
17 }
ChildrenPrice 技術分享圖片
 1 package shop;
 2 
 3 public class RegularPrice extends Price {
 4     @Override
 5     public int getPriceCode() {
 6         return Movie.REGULAR;
 7     }
 8 
 9     @Override
10     public double getCharge(int daysRent) {
11         double result = 2;
12         if(daysRent > 2){
13             result += (daysRent - 2) * 1.5;
14         }
15         return result;
16     }
17 }
RegularPrice

重構-改善既有代碼的設計 讀書筆記一