《重构-改善既有代码的设计》-第1例:租赁影片(1)
前些天發(fā)現(xiàn)了一個巨牛的人工智能學(xué)習(xí)網(wǎng)站,通俗易懂,風(fēng)趣幽默,忍不住分享一下給大家。點擊跳轉(zhuǎn)到教程。
買了《重構(gòu) - 改善既有代碼的設(shè)計?》一書,一直沒有好好看,大致過了下也覺得只是有點點印象而已,最后還是決定把代碼敲一次,記錄一下這些學(xué)習(xí)過程。
?
第一例:租賃影片
程序說明:顧客租了哪些影片,租期多長,根據(jù)租賃時間和影片類型算出費用和積分。
1. 分解并重組statement()?
原代碼如下有3個類。?
?
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;}}?
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;}}?
?
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 statement(){double totalAmount = 0; // 總租金 int frequentRenterPoints = 0; // 積分Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){double thisAmount = 0; // 租金Rental each = (Rental)rentals.nextElement();// 確定每種片子的租金switch(each.getMovie().getPriceCode()){case Movie.REGULAR:thisAmount += 2;if(each.getDaysRented() > 2 ){thisAmount += (each.getDaysRented() - 2) * 1.5;}break;case Movie.NEW_RELEASE:thisAmount += each.getDaysRented()*3;break;case Movie.CHILDRENS:thisAmount += 1.5;if(each.getDaysRented() > 3 ){thisAmount += (each.getDaysRented() - 3) * 1.5;}break;}// 增加積分frequentRenterPoints ++;// 新片+租賃時間達2天 積分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租賃記錄說明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thislAmount;}// 頁腳result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}}
1.1 抽離switch 語句到獨立方法
1.1.1 Customer類改為:
?
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 statement(){double totalAmount = 0; // 總租金 int frequentRenterPoints = 0; // 積分Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){double thisAmount = 0; // 租金Rental each = (Rental)rentals.nextElement();thisAmount = amountFor(each); // 計算租金// 增加積分frequentRenterPoints ++; // 新片+租賃時間達2天 積分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租賃記錄說明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 頁腳result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}// 計算租金private int amountFor(Rental each){int thisAmount = 0; // 租金// 確定每種片子的租金switch(each.getMovie().getPriceCode()){case Movie.REGULAR:thisAmount += 2;if(each.getDaysRented() > 2 ){thisAmount += (each.getDaysRented() - 2) * 1.5;}break;case Movie.NEW_RELEASE:thisAmount += each.getDaysRented()*3;break;case Movie.CHILDRENS:thisAmount += 1.5;if(each.getDaysRented() > 3 ){thisAmount += (each.getDaysRented() - 3) * 1.5;}break;}return thisAmount;}}?
?
1.1.2 注意?amountFor 方法的返回類型應(yīng)該是double類型:
?Customer類改為:
?
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 statement(){double totalAmount = 0; // 總租金 int frequentRenterPoints = 0; // 積分Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){double thisAmount = 0; // 租金Rental each = (Rental)rentals.nextElement();thisAmount = amountFor(each); // 計算租金// 增加積分frequentRenterPoints ++; // 新片+租賃時間達2天 積分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租賃記錄說明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 頁腳result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}// 計算租金private double amountFor(Rental each){double thisAmount = 0; // 租金// 確定每種片子的租金switch(each.getMovie().getPriceCode()){case Movie.REGULAR:thisAmount += 2;if(each.getDaysRented() > 2 ){thisAmount += (each.getDaysRented() - 2) * 1.5;}break;case Movie.NEW_RELEASE:thisAmount += each.getDaysRented()*3;break;case Movie.CHILDRENS:thisAmount += 1.5;if(each.getDaysRented() > 3 ){thisAmount += (each.getDaysRented() - 3) * 1.5;}break;}return thisAmount;}}
1.3 ?變量名稱應(yīng)該見名知意:好的代碼應(yīng)該清楚的表達出自己的功能,變量名稱是代碼清晰的一個關(guān)鍵。
?
amountFor方法中局部變量 thisAmount , 參數(shù)each 要改名。
改變量名 ,?Customer類改為:
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 statement(){double totalAmount = 0; // 總租金 int frequentRenterPoints = 0; // 積分Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){double thisAmount = 0; // 租金Rental each = (Rental)rentals.nextElement();thisAmount = amountFor(each); // 計算租金// 增加積分frequentRenterPoints ++; // 新片+租賃時間達2天 積分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租賃記錄說明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 頁腳result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}// 計算租金private double amountFor(Rental aRental){double result = 0; // 租金// 確定每種片子的租金switch(aRental.getMovie().getPriceCode()){case Movie.REGULAR:result += 2;if(aRental.getDaysRented() > 2 ){result += (aRental.getDaysRented() - 2) * 1.5;}break;case Movie.NEW_RELEASE:result += aRental.getDaysRented()*3;break;case Movie.CHILDRENS:result += 1.5;if(aRental.getDaysRented() > 3 ){result += (aRental.getDaysRented() - 3) * 1.5;}break;}return result;}}?
?
1.4 函數(shù)應(yīng)該放在它所使用的數(shù)據(jù)所屬的對象中, 顧客租金的計算應(yīng)該移動到Rental類中去。
?
?
?
Rental 類改為 : ?(計算租金方法去掉參數(shù)并改名 )
?
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;}// 計算租金double getCharge(){double result = 0; // 租金// 確定每種片子的租金switch(getMovie().getPriceCode()){case Movie.REGULAR:result += 2;if(getDaysRented() > 2 ){result += (getDaysRented() - 2) * 1.5;}break;case Movie.NEW_RELEASE:result += getDaysRented()*3;break;case Movie.CHILDRENS:result += 1.5;if(getDaysRented() > 3 ){result += (getDaysRented() - 3) * 1.5;}break;}return result;}}
原顧客類中amountFor方法直接調(diào)用計算租金方法即可,Customer 類改為:
?
?
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 statement(){double totalAmount = 0; // 總租金 int frequentRenterPoints = 0; // 積分Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){double thisAmount = 0; // 租金Rental each = (Rental)rentals.nextElement();thisAmount = amountFor(each); // 計算租金// 增加積分frequentRenterPoints ++; // 新片+租賃時間達2天 積分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租賃記錄說明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 頁腳result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}// 計算租金private double amountFor(Rental aRental){return aRental.getCharge();}}
1.5 ?去掉舊函數(shù)amountFor,直接調(diào)用新函數(shù)getCharge。Customer類改為 :?
?
?
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 statement(){double totalAmount = 0; // 總租金 int frequentRenterPoints = 0; // 積分Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){double thisAmount = 0; // 租金Rental each = (Rental)rentals.nextElement();thisAmount = each.getCharge(); // 計算租金// 增加積分frequentRenterPoints ++; // 新片+租賃時間達2天 積分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租賃記錄說明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 頁腳result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}}
1.6 ?盡量去掉臨時變量,臨時變量會導(dǎo)致大量參數(shù)的傳遞,沒有必要。 thisAmount 是個多余的臨時變量,直接去掉。
?
Customer?改為:
?
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 statement(){double totalAmount = 0; // 總租金 int frequentRenterPoints = 0; // 積分Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();// 增加積分frequentRenterPoints ++; // 新片+租賃時間達2天 積分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租賃記錄說明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(each.getCharge())+"\n";totalAmount += each.getCharge();}// 頁腳result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}}?
1.7 把積分計算方法放到Rental類中,寫為常客積分計算方法(getFrequentRenterPoints),并改變Customer類中積分計算代碼 。
?
Rental類改為:
?
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;}// 計算租金double getCharge(){double result = 0; // 租金// 確定每種片子的租金switch(getMovie().getPriceCode()){case Movie.REGULAR:result += 2;if(getDaysRented() > 2 ){result += (getDaysRented() - 2) * 1.5;}break;case Movie.NEW_RELEASE:result += getDaysRented()*3;break;case Movie.CHILDRENS:result += 1.5;if(getDaysRented() > 3 ){result += (getDaysRented() - 3) * 1.5;}break;}return result;}// 常客積分計算int getFrequentRenterPoints(){// 增加積分int frequentRenterPoints =0;frequentRenterPoints++; // (新片+租賃時間達2天 積分+1 )if(getMovie().getPriceCode() == Movie.NEW_RELEASE && getDaysRented() > 1){frequentRenterPoints ++;}return frequentRenterPoints;}}
Customer類中statement方法改為:
?
?
/*** 生成訂單* @return*/public String statement(){double totalAmount = 0; // 總租金 int frequentRenterPoints = 0; // 積分Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();// 積分frequentRenterPoints += each.getFrequentRenterPoints();// 本次租賃記錄說明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(each.getCharge())+"\n";totalAmount += each.getCharge();}// 頁腳result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}?
1.8 去掉statement方法中的2個臨時變量:totalAmount 和?frequentRenterPoints 。抽離出對應(yīng)計算方法,并調(diào)用。
?
Customer?類改為:
?
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 statement(){Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();// 本次租賃記錄說明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(each.getCharge())+"\n";}// 頁腳result +="Amount owed is "+ String.valueOf(getTotalCharge())+"\n";result +="You eared "+String.valueOf(getTotalFrequentRenterPoints())+"frequent renter points";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;}}?
本步改寫作用說明:
?
?這里雖然從1個循環(huán)變?yōu)?個,但是多了2個查詢函數(shù)。
1) ?使得Customer 類中的任何代碼都可以調(diào)用這些查詢函數(shù)。
2) ?若系統(tǒng)其它部分需要這些信息,也可以輕松地將查詢函數(shù)加入?Customer 類接口。而若沒有這些查詢波函數(shù),其它函數(shù)就必須了解 ?Rental 類,并自行建立循環(huán)。
?
?
1.9 加功能:打印憑條。
statement 方法改為 htmlStatement :
?
?
/*** 生成訂單(打印憑條)* @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;}?
?
?
?
?
未完,見 ?:?重構(gòu)-改善既有代碼的設(shè)計-第1例:租賃影片(2)
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的《重构-改善既有代码的设计》-第1例:租赁影片(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 十条技巧 更聪明地使用Google搜索
- 下一篇: [转]删除表中重复记录