《重构-改善既有代码的设计》-第1例:租赁影片(2)
前些天發(fā)現(xiàn)了一個巨牛的人工智能學(xué)習(xí)網(wǎng)站,通俗易懂,風(fēng)趣幽默,忍不住分享一下給大家。點擊跳轉(zhuǎn)到教程。
上接 ?重構(gòu)-改善既有代碼的設(shè)計-第1例:租賃影片(1)
?
2 ?運用多態(tài)取代與價格相關(guān)的條件邏輯
?
2.1 最好不要在另一個對象的屬性基礎(chǔ)上運用switch語句,應(yīng)該在對象自己的數(shù)據(jù)上使用。
2.1.1 移動 getCharge ,getFrequentRenterPoints 方法到Movie 類中去。把會根據(jù)影片類型的變化而變化的東西放在影片類中。
Movie 類改為:
?
package bean;/*** 影片* @author Administrator*/ public class Movie {public static final int CHILDRENS = 2; // 兒童片public static final int REGULAR = 0; // 普通片public static final int NEW_RELEASE = 1; // 新片private String _title;private int _priceCode;public Movie(String _title, int _priceCode) {this._title = _title;this._priceCode = _priceCode;}public int getPriceCode() {return _priceCode;}public void setPriceCode(int _priceCode) {this._priceCode = _priceCode;}public String getTitle() {return _title;}/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/double getCharge(int dayRented){double result = 0; // 租金// 確定每種片子的租金switch(getPriceCode()){case Movie.REGULAR:result += 2;if(dayRented > 2 ){result += (dayRented - 2) * 1.5;}break;case Movie.NEW_RELEASE:result += dayRented*3;break;case Movie.CHILDRENS:result += 1.5;if(dayRented > 3 ){result += (dayRented - 3) * 1.5;}break;}return result;}/*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/int getFrequentRenterPoints(int dayRented){// (新片+租賃時間達2天 積分+1 )if(getPriceCode() == Movie.NEW_RELEASE && dayRented > 1){return 2;}else{return 1;}}}?
?
?
?
?
?
2.1.2 修改Rental 類中的 ?getCharge ,getFrequentRenterPoints方法,讓它調(diào)用Movie 類提供的新函數(shù)。
?
Rental 類中的計算租金方法和常客積分計算方法 改為:
?
/*** 常客積分計算* @return*/int getFrequentRenterPoints(){return _movie.getFrequentRenterPoints(_daysRented);}/*** 計算租金* @return*/double getCharge(){return _movie.getCharge(_daysRented);}?
?
?
?
?
2.2 ?為了確保任何時候都要通過取值函數(shù)和賦值函數(shù)來訪問 不愿意被外界直接訪問的屬性,我們用一個對賦值函數(shù)的調(diào)用來代替構(gòu)造中的部分代碼。
Movie 類的構(gòu)造之前為:
?
private String _title;private int _priceCode;public Movie(String _title, int _priceCode) {this._title = _title;this._priceCode = _priceCode;}public int getPriceCode() {return _priceCode;}public void setPriceCode(int _priceCode) {this._priceCode = _priceCode;}public String getTitle() {return _title;}
讓構(gòu)造不能直接訪問?不愿意被外界直接訪問的屬性,構(gòu)造現(xiàn)在改為:
?
?
public Movie(String _title, int _priceCode) {this._title = _title;setPriceCode(_priceCode);}?
?
?
?
?
2.3 有新需求到來,有新品種影片,租金計算又有新算法 。
用到設(shè)計模式的狀態(tài)模式State(對象行為型)。
于是新建一個Price抽象類,并在其內(nèi)給2個抽象方法用于獲取影片的計價類型和計算租金,積分計算。
再寫多個子類繼承Price并各自實現(xiàn)父類方法以實現(xiàn)對租金計算,積分計算的重構(gòu)。
?2.3.1 Price 及子類 :
?
package bean;/*** 租金* @author Administrator*/ public abstract class Price {abstract int getPriceCode(); }?
package bean;/*** 兒童片租金* @author Administrator*/ public class ChildrensPrice extends Price {@Overrideint getPriceCode() {return Movie.CHILDRENS;}}?
package bean;public class NewReleasePrice extends Price {@Overrideint getPriceCode() {return Movie.NEW_RELEASE;}}?
package bean;/*** 普通片租金* @author Administrator*/ public class RegularPrice extends Price {@Overrideint getPriceCode() {return Movie.REGULAR;}}?
?
?
?
?
2.3.2 ?修改Movie ?類的計價類型屬性為Price類型,并改寫賦值函數(shù) :
?
package bean;/*** 影片* @author Administrator*/ public class Movie {public static final int CHILDRENS = 2; // 兒童片public static final int REGULAR = 0; // 普通片public static final int NEW_RELEASE = 1; // 新片private String _title;private Price _price; public Movie(String _title, int _priceCode) {this._title = _title;setPriceCode(_priceCode);}public int getPriceCode() {return _price.getPriceCode();}public void setPriceCode(int _priceCode) {switch(_priceCode){case REGULAR:_price = new RegularPrice();break;case NEW_RELEASE:_price = new NewReleasePrice();break;case CHILDRENS:_price = new ChildrensPrice();break;default:throw new IllegalArgumentException(" Incorrect PriceCode! ");}} ...?
?
2.3.4 ?把租金計算方法移動到 Price 類,在Movie 類中調(diào)用即可。
?
?
?
package bean;/*** 租金* @author Administrator*/ public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/double getCharge(int dayRented){double result = 0; // 租金// 確定每種片子的租金switch(getPriceCode()){case Movie.REGULAR:result += 2;if(dayRented > 2 ){result += (dayRented - 2) * 1.5;}break;case Movie.NEW_RELEASE:result += dayRented*3;break;case Movie.CHILDRENS:result += 1.5;if(dayRented > 3 ){result += (dayRented - 3) * 1.5;}break;}return result;} }?
/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/double getCharge(int dayRented){return _price.getCharge(dayRented); // 這是在 Movie 類中的調(diào)用}?
?
2.3.5 ?重構(gòu)租金計算方法,把每個getCharge 方法中switch 的每個 case 取出,在相應(yīng)的Price子類中寫一個覆蓋函數(shù)。
?
?
最后把Price的 租金計算方法改為抽象方法。
租金類 Price 重構(gòu)前:
?
package bean;/*** 租金* @author Administrator*/ public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/double getCharge(int dayRented){double result = 0; // 租金// 確定每種片子的租金switch(getPriceCode()){case Movie.REGULAR:result += 2;if(dayRented > 2 ){result += (dayRented - 2) * 1.5;}break;case Movie.NEW_RELEASE:result += dayRented*3;break;case Movie.CHILDRENS:result += 1.5;if(dayRented > 3 ){result += (dayRented - 3) * 1.5;}break;}return result;} }
? 重構(gòu)getCharge 方法后Price類 及子類 為:
?
?
package bean;/*** 租金* @author Administrator*/ public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/abstract double getCharge(int dayRented);}?
package bean;/*** 兒童片租金* @author Administrator*/ public class ChildrensPrice extends Price {@Overrideint getPriceCode() {return Movie.CHILDRENS;}/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/@Overridedouble getCharge(int dayRented){double result = 1.5;if(dayRented > 3){result += (dayRented - 3) * 1.5;}return result;} }?
package bean; /*** 新片租金* @author Administrator*/ public class NewReleasePrice extends Price {@Overrideint getPriceCode() {return Movie.NEW_RELEASE;}/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/@Overridedouble getCharge(int dayRented){return dayRented*3;}}?
package bean;/*** 普通片租金* @author Administrator*/ public class RegularPrice extends Price {@Overrideint getPriceCode() {return Movie.REGULAR;}/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/@Overridedouble getCharge(int dayRented){double result = 2;if(dayRented > 2){result += (dayRented - 2) * 1.5;}return result;} }?
2.3.6 對積分計算方法作相同重構(gòu)。
從 Movie 類中移動積分計算方法到 Price 類中。Movie 類中調(diào)用Proce的積分計算方法就行了。
?
package bean;/*** 租金* @author Administrator*/ public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/abstract double getCharge(int dayRented);/*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/int getFrequentRenterPoints(int dayRented){// (新片+租賃時間達2天 積分+1 )if(getPriceCode() == Movie.NEW_RELEASE && dayRented > 1){return 2;}else{return 1;}} }?
package bean;/*** 影片* @author Administrator*/ public class Movie {public static final int CHILDRENS = 2; // 兒童片public static final int REGULAR = 0; // 普通片public static final int NEW_RELEASE = 1; // 新片private String _title;private Price _price; public Movie(String _title, int _priceCode) {this._title = _title;setPriceCode(_priceCode);}public int getPriceCode() {return _price.getPriceCode();}.../*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/int getFrequentRenterPoints(int dayRented){return _price.getFrequentRenterPoints(dayRented);}} .../*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/int getFrequentRenterPoints(int dayRented){return _price.getFrequentRenterPoints(dayRented);}}?
?
對 Proce 類的積分計算方法重構(gòu),只是為新片類型增加一個覆寫函數(shù),并在超類中保留原函數(shù),使它成為一種默認行為。
?
?
?
package bean;/*** 租金* @author Administrator*/ public abstract class Price {abstract int getPriceCode();.../*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/int getFrequentRenterPoints(int dayRented){// 默認積1分return 1;} }?
?
package bean; /*** 新片租金* @author Administrator*/ public class NewReleasePrice extends Price {.../*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/@Overrideint getFrequentRenterPoints(int dayRented){// (新片+租賃時間達2天 積分+1 )return (dayRented > 1) ? 2 : 1 ;} } .../*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/@Overrideint getFrequentRenterPoints(int dayRented){// (新片+租賃時間達2天 積分+1 )return (dayRented > 1) ? 2 : 1 ;} }?
到此,重構(gòu)-改善既有代碼的設(shè)計-第1例:租賃影片,就重構(gòu)完成了。
總結(jié) :這樣重構(gòu)以后,不論是修改影片分類結(jié)構(gòu),還是修改租金計算規(guī)則又或積分計算規(guī)則就都容易多了 。
?
注:個人覺得 Movie 類中的?setPriceCode 方法 ?中得每種 price 的時候不該用構(gòu)造函數(shù),而是該直接調(diào)用各Price 子類 中的 getPriceCode 方法。
但此博文尊重原書中代碼未作改動。
?
最后 所有類完整代碼為:
?
package bean;import java.util.Enumeration; import java.util.Vector;/*** 顧客* @author Administrator*/ public class Customer{private String _name; // 顧客名字private Vector _rentals = new Vector(); // 租賃訂單數(shù)組public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成訂單(打印憑條)* @return*/public String htmlStatement(){Enumeration rentals = _rentals.elements();String result = "<P><H1>Rentals for <EM> "+ getName() + "</EM></H1></P>\n";while( rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();// 本次租賃記錄說明result += each.getMovie().getTitle()+":"+ String.valueOf(each.getCharge())+"<BR>\n";}// 頁腳result +="<P>You owe <EM>"+ String.valueOf(getTotalCharge())+"</EM></P>\n";result +="<P> on this rental you earned <EM> "+String.valueOf(getTotalFrequentRenterPoints())+"</EM> frequent renter points </P>";return result;}// 計算總積分private int getTotalFrequentRenterPoints(){int result = 0;Enumeration rentals = _rentals.elements();while(rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();result += each.getFrequentRenterPoints();}return result;}// 計算總租金private double getTotalCharge(){double result = 0;Enumeration rentals = _rentals.elements();while(rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();result += each.getCharge();}return result;}}?
?
package bean; /*** 租賃訂單* @author Administrator*/ public class Rental {private Movie _movie ; // 影片private int _daysRented; // 租賃天數(shù)public Rental(Movie _movie, int _daysRented) {this._movie = _movie;this._daysRented = _daysRented;}public Movie getMovie() {return _movie;}public int getDaysRented() {return _daysRented;}/*** 常客積分計算* @return*/int getFrequentRenterPoints(){return _movie.getFrequentRenterPoints(_daysRented);}/*** 計算租金* @return*/double getCharge(){return _movie.getCharge(_daysRented);}}?
package bean;/*** 影片* @author Administrator*/ public class Movie {public static final int CHILDRENS = 2; // 兒童片public static final int REGULAR = 0; // 普通片public static final int NEW_RELEASE = 1; // 新片private String _title;private Price _price; public Movie(String _title, int _priceCode) {this._title = _title;setPriceCode(_priceCode);}public int getPriceCode() {return _price.getPriceCode();}public void setPriceCode(int _priceCode) {switch(_priceCode){case REGULAR:_price = new RegularPrice();break;case NEW_RELEASE:_price = new NewReleasePrice();break;case CHILDRENS:_price = new ChildrensPrice();break;default:throw new IllegalArgumentException(" Incorrect PriceCode! ");}}public String getTitle() {return _title;}/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/double getCharge(int dayRented){return _price.getCharge(dayRented);}/*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/int getFrequentRenterPoints(int dayRented){return _price.getFrequentRenterPoints(dayRented);}}?
package bean;/*** 租金+積分* @author Administrator*/ public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/abstract double getCharge(int dayRented);/*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/int getFrequentRenterPoints(int dayRented){// 默認積1分return 1;} }?
package bean;/*** 兒童片租金* @author Administrator*/ public class ChildrensPrice extends Price {@Overrideint getPriceCode() {return Movie.CHILDRENS;}/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/@Overridedouble getCharge(int dayRented){double result = 1.5;if(dayRented > 3){result += (dayRented - 3) * 1.5;}return result;} }?
package bean; /*** 新片租金* @author Administrator*/ public class NewReleasePrice extends Price {@Overrideint getPriceCode() {return Movie.NEW_RELEASE;}/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/@Overridedouble getCharge(int dayRented){return dayRented*3;}/*** 常客積分計算* @param dayRented 租賃天數(shù)* @return*/@Overrideint getFrequentRenterPoints(int dayRented){// (新片+租賃時間達2天 積分+1 )return (dayRented > 1) ? 2 : 1 ;} } package bean;/*** 普通片租金* @author Administrator*/ public class RegularPrice extends Price {@Overrideint getPriceCode() {return Movie.REGULAR;}/*** 計算租金* @param dayRented 租賃天數(shù)* @return*/@Overridedouble getCharge(int dayRented){double result = 2;if(dayRented > 2){result += (dayRented - 2) * 1.5;}return result;} }?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的《重构-改善既有代码的设计》-第1例:租赁影片(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle biee
- 下一篇: 狗窝里的小日子- 4 ...