《重構-改善既有代碼的設計》-第1例:租賃影片(2)

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。

上接 ?重構-改善既有代碼的設計-第1例:租賃影片(1)

?

2 ?運用多態取代與價格相關的條件邏輯

?

2.1 最好不要在另一個對象的屬性基礎上運用switch語句,應該在對象自己的數據上使用。

2.1.1 移動 getCharge ,getFrequentRenterPoints 方法到Movie 類中去。把會根據影片類型的變化而變化的東西放在影片類中。

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 租賃天數* @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 租賃天數* @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方法,讓它調用Movie 類提供的新函數。

?

Rental 類中的計算租金方法和常客積分計算方法 改為:

?

	/*** 常客積分計算* @return*/int getFrequentRenterPoints(){return _movie.getFrequentRenterPoints(_daysRented);}/*** 計算租金* @return*/double getCharge(){return _movie.getCharge(_daysRented);}

?

?

?

?

?

2.2 ?為了確保任何時候都要通過取值函數和賦值函數來訪問 不愿意被外界直接訪問的屬性,我們用一個對賦值函數的調用來代替構造中的部分代碼。

Movie 類的構造之前為:

?

	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;}


讓構造不能直接訪問?不愿意被外界直接訪問的屬性,構造現在改為:

?

?

public Movie(String _title, int _priceCode) {this._title = _title;setPriceCode(_priceCode);}

?

?

?

?

?

2.3 有新需求到來,有新品種影片,租金計算又有新算法 。

用到設計模式的狀態模式State(對象行為型)。

于是新建一個Price抽象類,并在其內給2個抽象方法用于獲取影片的計價類型和計算租金,積分計算。

再寫多個子類繼承Price并各自實現父類方法以實現對租金計算,積分計算的重構。

?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類型,并改寫賦值函數 :

?

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 類中調用即可。

?

?

?

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數* @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 租賃天數* @return*/double getCharge(int dayRented){return _price.getCharge(dayRented); // 這是在 Movie 類中的調用}

?

?

2.3.5 ?重構租金計算方法,把每個getCharge 方法中switch 的每個 case 取出,在相應的Price子類中寫一個覆蓋函數。

?

?

最后把Price的 租金計算方法改為抽象方法。

租金類 Price 重構前:

?

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數* @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;}
}


? 重構getCharge 方法后Price類 及子類 為:

?

?

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數* @return*/abstract double getCharge(int dayRented);}

?

package bean;/*** 兒童片租金* @author Administrator*/
public class ChildrensPrice extends Price {@Overrideint getPriceCode() {return Movie.CHILDRENS;}/*** 計算租金* @param dayRented 租賃天數* @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 租賃天數* @return*/@Overridedouble getCharge(int dayRented){return dayRented*3;}}

?

package bean;/*** 普通片租金* @author Administrator*/
public class RegularPrice extends Price {@Overrideint getPriceCode() {return Movie.REGULAR;}/*** 計算租金* @param dayRented 租賃天數* @return*/@Overridedouble getCharge(int dayRented){double result = 2;if(dayRented > 2){result += (dayRented - 2) * 1.5;}return result;}
}

?

2.3.6 對積分計算方法作相同重構。

從 Movie 類中移動積分計算方法到 Price 類中。Movie 類中調用Proce的積分計算方法就行了。

?

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數* @return*/abstract double getCharge(int dayRented);/*** 常客積分計算* @param dayRented 租賃天數* @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 租賃天數* @return*/int getFrequentRenterPoints(int dayRented){return _price.getFrequentRenterPoints(dayRented);}}
	.../*** 常客積分計算* @param dayRented 租賃天數* @return*/int getFrequentRenterPoints(int dayRented){return _price.getFrequentRenterPoints(dayRented);}}

?

?

對 Proce 類的積分計算方法重構,只是為新片類型增加一個覆寫函數,并在超類中保留原函數,使它成為一種默認行為。

?

?

?

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();.../*** 常客積分計算* @param dayRented 租賃天數* @return*/int getFrequentRenterPoints(int dayRented){// 默認積1分return 1;}
}

?

?

package bean;
/*** 新片租金* @author Administrator*/
public class NewReleasePrice extends Price {.../*** 常客積分計算* @param dayRented 租賃天數* @return*/@Overrideint getFrequentRenterPoints(int dayRented){// (新片+租賃時間達2天  積分+1 )return (dayRented > 1) ? 2 : 1 ;}
}
	.../*** 常客積分計算* @param dayRented 租賃天數* @return*/@Overrideint getFrequentRenterPoints(int dayRented){// (新片+租賃時間達2天  積分+1 )return (dayRented > 1) ? 2 : 1 ;}
}

?

到此,重構-改善既有代碼的設計-第1例:租賃影片,就重構完成了。
總結 :這樣重構以后,不論是修改影片分類結構,還是修改租金計算規則又或積分計算規則就都容易多了 。

?

注:個人覺得 Movie 類中的?setPriceCode 方法 ?中得每種 price 的時候不該用構造函數,而是該直接調用各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();  // 租賃訂單數組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; // 租賃天數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 租賃天數* @return*/double getCharge(int dayRented){return _price.getCharge(dayRented);}/*** 常客積分計算* @param dayRented 租賃天數* @return*/int getFrequentRenterPoints(int dayRented){return _price.getFrequentRenterPoints(dayRented);}}

?

package bean;/*** 租金+積分* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 計算租金* @param dayRented 租賃天數* @return*/abstract double getCharge(int dayRented);/*** 常客積分計算* @param dayRented 租賃天數* @return*/int getFrequentRenterPoints(int dayRented){// 默認積1分return 1;}
}

?

package bean;/*** 兒童片租金* @author Administrator*/
public class ChildrensPrice extends Price {@Overrideint getPriceCode() {return Movie.CHILDRENS;}/*** 計算租金* @param dayRented 租賃天數* @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 租賃天數* @return*/@Overridedouble getCharge(int dayRented){return dayRented*3;}/*** 常客積分計算* @param dayRented 租賃天數* @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 租賃天數* @return*/@Overridedouble getCharge(int dayRented){double result = 2;if(dayRented > 2){result += (dayRented - 2) * 1.5;}return result;}
}

?

?

?

?

?

?

?

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/451624.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/451624.shtml
英文地址,請注明出處:http://en.pswp.cn/news/451624.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

elasticsearch 的查詢 /_nodes/stats 各字段意思

/_nodes/stats 字段意思 “” 1 { 2 "_nodes": {3 "total": 1,4 "successful": 1,5 "failed": 06 },7 "cluster_name": "ELKTEST",8 "nodes": {9 "lnlHC8yERCKXCuAc…

看完Java的動態代理技術——Pythoner笑了

Java的動態代理常用來包裝原始方法調用&#xff0c;用于增強或改寫現有方法的邏輯&#xff0c;它在Java技術領域被廣為使用&#xff0c;在阿里的Sofa RPC框架序列化中你能看到它的身影&#xff0c;Hibernate的實體類功能增強也是以動態代理的方式解決的&#xff0c;還有Spring吹…

shell實現從1加到100

#!/bin/bash # test"while do done"PATH/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATHsum0 for i in $(seq 1 100); do let sumi done echo "The sum is > $sum "

面試常考的數據結構Java實現

1、線性表 2、線性鏈表 3、棧 4、隊列 5、串 6、數組 7、廣義表 8、樹和二叉樹 二叉樹&#xff1a;每個結點至多只有兩棵子樹&#xff08;即二叉樹中不存在度大于2的結點&#xff09;&#xff0c;并且&#xff0c;二叉樹的子樹有左右之分&#xff0c;其次序不能任意顛倒。 二叉…

Java5線程并發庫之LOCK(鎖)CONDITION(條件)實現線程同步通信

為什么80%的碼農都做不了架構師&#xff1f;>>> Lock&#xff08;鎖&#xff09;&Condition&#xff08;條件&#xff09;實現線程同步通信 接下來介紹&#xff0c;java5線程并發庫里面的鎖。跟鎖有關的類和接口主要是位于java.util.concurrent.locks包。 Lock…

互聯網,可預見的未來

我記憶中的1998年代&#xff0c;PC迅猛發展&#xff0c;CPU速度逐年翻番&#xff0c;持續了7年&#xff0c;但下一個7年到現在&#xff0c;基本上沒有太大提升&#xff1b;顯示器從14英寸CRT發展到2005的21英寸LED&#xff0c;后來也沒有繼續進化。為什么&#xff1f;當人對計算…

什么時候用GET?什么時候用POST?

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、 GET和POST兩種方法都是將數據送到服務器&#xff0c;但你該用哪一種呢&#xff1f; HTTP標準包含這兩種方法是為了達到不同的目的…

邏輯運算符與邏輯表達式

1 #include <stdio.h>2 3 int main()4 {5 int a0;int b0;6 if(a&&b)//a&&ba的邏輯值為0&#xff0c;則執行else7 {8 printf("a&&b is true\n");9 } 10 else 11 { 12 printf("a&&…

linux/shell相關知識點

阿里Linux Shell腳本面試25個經典問答 Linux運維工程師12道面試題整理 感謝作者分享&#xff01;

20180601]函數與標量子查詢2.txt

[20180601]函數與標量子查詢2.txt --//昨天看http://www.cnblogs.com/kerrycode/p/9099507.html鏈接,里面提到: 通俗來將&#xff0c;當使用標量子查詢的時候&#xff0c;ORACLE會將子查詢結果緩存在哈希表中&#xff0c; 如果后續的記錄出現同樣的值&#xff0c;優化器通過緩存…

ODP 使用 ArrayBind 時可能會遇到的巨坑 'System.IConvertible' 的解決方法

Unable to cast object of type System.Nullable1[System.Int16][] to type System.IConvertible 一段代碼99%不會出錯&#xff0c;0.1%會報上邊的錯&#xff0c;debug費了老鼻子時間&#xff0c;發現此坑很深。異常是 cmd.ExecuteNonQuery() 拋的&#xff0c;實際是 para.Valu…

eclipse快速定位到錯誤處

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程 以前都是按著滾動條往下拉&#xff0c;找到錯誤的地方&#xff0c;有時比較多的時候就很麻煩。 其實eclipse是可以直接快速定位的&#x…

C語言中的“”和“”

先說左移,左移就是把一個數的所有位都向左移動若干位,在C中用<<運算符.例如: int i 1; i i << 2; //把i里的值左移2位 也就是說,1的2進制是000...0001(這里1前面0的個數和int的位數有關,32位機器,gcc里有31個0),左移2位之后變成 000...0100,也就是10進制的4,所以…

網站性能優化的三重境界

這篇文章是關于網站性能優化體驗的&#xff0c;性能優化是一個復雜的話題&#xff0c;牽涉的東西非常多&#xff0c;我只是按照我的理解列出了性能優化整個過程中需要考慮的種種因素。點到為止&#xff0c;包含的內容以淺顯的介紹為主&#xff0c;如果你有見解能告知我那再好不…

Linux使用RSA實現免密登錄(原理)

參考文獻Linux密鑰rsa加密原理和ssh使用密鑰實現免密碼登錄 感謝作者分享&#xff01;

PYTHON 爬蟲筆記十一:Scrapy框架的基本使用

Scrapy框架詳解及其基本使用 scrapy框架原理 Scrapy是一個為了爬取網站數據&#xff0c;提取結構性數據而編寫的應用框架。 其可以應用在數據挖掘&#xff0c;信息處理或存儲歷史數據等一系列的程序中。其最初是為了頁面抓取 (更確切來說, 網絡抓取 )所設計的&#xff0c; 也可…

java設計把兩個字符串的值交換 而不使用中間變量

public class Test {public static void main(String[] args) {String s1 "aaa";String s2 "cccx";s1 s1 s2;s2 s1.substring(0, s1.length()-s2.length());s1 s1.substring(s2.length());System.out.println(s1" - "s2);}}

服務器返回值 解釋 ajax提交方式 后臺數據刷進前端

轉載于:https://www.cnblogs.com/liuliang389897172/p/9120715.html

no typehandler found for property XXXX 解決

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. ssm框架下 啟動服務報錯如題。 2. 原因&#xff1a; 我的情況是&#xff0c;代碼中實體屬性映射書寫和數據庫字段名字不一致。 數據…

C++主流預處理,編譯和鏈接過程

在C的程序的編寫過程中&#xff0c;基本上都碰到過LNK2005的錯誤吧&#xff0c;下面就針對這個問題詳細分析&#xff1a;首先&#xff0c;預處理階段&#xff1a;這一過程&#xff0c;主要針對#include和#define進行處理&#xff0c;具體過程如下&#xff1a;對于cpp文件中經常…