重構-改善既有代碼的設計 讀書筆記一
阿新 • • 發佈:2019-05-03
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
重構-改善既有代碼的設計 讀書筆記一