重構—改善既有程式的設計(chapter 1)
-
Upload
chris-huang -
Category
Education
-
view
3.276 -
download
4
description
Transcript of 重構—改善既有程式的設計(chapter 1)
![Page 1: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/1.jpg)
RefactoringChapter 1: 重構,第一個案例
Kalin Chih
![Page 2: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/2.jpg)
何時該考慮要重構 ?如果發現無法很方便地增加新 feature ,或修改邏輯作法 :
先找看看是否有一套可靠的測試機制 ?先重構程式再增加新 feature
![Page 3: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/3.jpg)
重構 Step1: 了解案例案例 : 影片出租店程式
pirceCode: 決定影片類型、價錢daysRented: 決定租片金額、積點statement(): 列印租片紀錄清單與總金額、總積點
Example: chp1.movie_rental.v1.* Test Class: chp1.movie_rental.test.Test.java
![Page 4: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/4.jpg)
重構 Step2: Extract 金額計算動機 : 過長的程式碼不易閱讀
Extract 過長的程式區塊 (Fine-grained)
作法 : Extract Method將 Customer.statement() 的內金額計算程式區塊,
Extract 成一個新 anmountFor()
Example : chp1.movie_rental.v2.extract_amount.*
重構前,有一套可靠的測試機制是很重要滴 ~
![Page 5: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/5.jpg)
Rename 是值得的行為電腦可以理解和人寫出來的程式碼,但唯有寫出人類
容易理解的程式碼,才是優秀的程式員
![Page 6: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/6.jpg)
重構 Step3: Move 金額計算動機 : 計算金額的程式碼為何不是放在 Rental 而是
放在 Customer?大多情況下, method 應該放在他所使用的 object
(class) 內金額計算應該放在 Rental 比放在 Customer 洽當
作法 : Move Method將 Customer.statement() 內的計算金額的程式碼搬
到 Rental
Example: chp1.movie_rental.v3.move_amount.*
![Page 7: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/7.jpg)
重構 Step4: Move 點數計算動機 : 類似於金額計算,點數計算功能應該要屬於
Rental 比較洽當作法 :
將 Customer.statement() 內的點數計算程式碼搬到 Rental
Example: chp1.movie_rental.v4.move_frequentrenterpoints.*
![Page 8: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/8.jpg)
重構 Step5: 移除 Temp Variables動機 : Temp Variable 可能會是個問題,助長程式碼過長
由於變數只是暫時的,所以能見度只在這個程式區塊。若在其他區塊也想要使用這個 Temp Variable ,便會驅使寫出更長的程式。
作法 : 使用 Query Method 取代 temp variable若使用 Query Method 取代 Temp Variable ,至少同一個 class 內
都能獲得這份資訊。getTotalCharge() 取代 totalAmountgetFrequentRenterPoints() 取代 frequentRenterPoints
Example: chp1.movie_rental.v5.replace_temp_with_query.*
New issue: 效能變差原本只要一個迴圈做到的事情,現在要跑三個迴圈才能做到
![Page 9: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/9.jpg)
關於 Performance 這件事…Performance 應該是在最佳化效能時再來擔心,在
重構的階段先不用擔心這件事情Performance 應該是透過工具來檢測,肉眼是很難
看出來的回想重構的目的…
讓程式更容易被讀取讓程式更容易被擴充
The best way to optimize performance is to first write a well factored program, then optimize it
![Page 10: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/10.jpg)
重構後…
現在可以透過這些 Query Methods 來取得資訊,而不需研究細節
![Page 11: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/11.jpg)
重構 Step6: Move 金額計算、點數計算動機 : Rental 必須取得 Movie 才能運算金額。 ( 物件應該在自
己的資料上運算邏輯,而不是拿別人的資料來運算 )
考慮該邏輯該屬於哪個物件影片的價格邏輯是應該放到 Movie class 內來計算比較洽當
作法 : Move Method將 getCharge() 與 getFrequentRenterPoints() 移到 Movie
class Example: chp1.movie_rental.v6.move_selfobject
public class Rental {double getCharge() {
double result = 0;// 取得影片的出租價格switch (getMovie().getPriceCode()) {
// 普通片case Movie.REGULAR:
![Page 12: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/12.jpg)
So Far So Good…寫了幾個新的 Query Methods 可以 reuse程式碼更容易閱讀
But! 計算金額的那段 switch 還是不利於將來計費邏輯的變動
![Page 13: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/13.jpg)
考慮繼承…使用 Polymorphism 來取代 switch
問題來了,若之後 Movie 有其他類似 getCharge() 容易變動的邏輯,就很不利於維護
![Page 14: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/14.jpg)
使用 Strategy Pattern
Strategy Pattern 封裝了會變動的演算法 (Price subclasses) ,所以不會影響使用演算法的程式(Movie)
![Page 15: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/15.jpg)
重構 Step7: Replace Type Code with State/Strategy作法 (1/2):
Replace Type Code with State/Strategy: 將 type code behavior 搬移到 Strategy Pattern 讓金額計算的邏輯不再綁死在 Movie ,所以將金額計算的邏輯搬移到
Price 家族
Self Encapsulate Field 時機 : 當想要存取一個 super class 的 field ,卻在 sub class 改變
它的 value 作法 : 為 field 建立 setter 與 getter ,只用這些 methods 來存取
這個 field( 即使在同一個 class 內 )
Move Method Example: chp1.movie_rental.v7.strategy_pattern
![Page 16: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/16.jpg)
重構 Step8: Replace Conditional with Polymorphism作法 (2/2):
Replace Conditional with Polymorphism: 在 sub Price classes 內建立 overriding method 來取代 super Price
class 的 switch將 super Price class 的 getCharge() 改為 abstract
Example: chp1.movie_rental.v7.strategy_pattern 方便 : 為了積點的預設值,所以不將 getFrequentRenterPoints() 也改成 abstract
public class NewReleasePrice extends Price {
double getCharge(int daysRented) {
return (double) daysRented * 3;}
public abstract class Price {double getCharge(int daysRented) {
double result = 0;switch (getPriceCode()) {
case Movie.NEW_RELEASE:result += (double) daysRented * 3;break;
}return result;
}
![Page 17: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/17.jpg)
Final Class Diagram
![Page 18: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/18.jpg)
重構後的好處讓程式碼更容易閱讀
Rename MethodExtract MethodMove Method
新的 Query Methods 可以 reuseReplace Temp with Query
將最容易變動的邏輯抽離出來Replace Type Code with State/StrategySelf Encapsulate FieldReplace Conditional with Polymorphism
![Page 19: 重構—改善既有程式的設計(chapter 1)](https://reader036.fdocument.pub/reader036/viewer/2022081802/548e735cb4795968148b4788/html5/thumbnails/19.jpg)
Thank You!Slide & Example Code URL:
xxxx