Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Refactoring<br />Chapter 1: 重構,第一個案例<br />KalinChih<br />
何時該考慮要重構?<br />如果發現無法很方便地增加新 feature,或修改邏輯<br />作法:<br />先找看看是否有一套可靠的測試機制?<br />先重構程式<br />再增加新 feature<br />
重構Step1: 了解案例<br />案例: 影片出租店程式<br />pirceCode: 決定影片類型、價錢<br />daysRented: 決定租片金額、積點<br />statement(): 列印租片紀錄清單與總金額、總積點<br ...
重構Step2: Extract 金額計算<br />動機: 過長的程式碼不易閱讀<br />Extract 過長的程式區塊 (Fine-grained)<br />作法:Extract Method<br />將 Customer.state...
Rename 是值得的行為<br />電腦可以理解和人寫出來的程式碼,但唯有寫出人類容易理解的程式碼,才是優秀的程式員<br />
重構Step3: Move金額計算<br />動機: 計算金額的程式碼為何不是放在 Rental 而是放在 Customer?<br />大多情況下,method 應該放在他所使用的 object (class) 內<br />金額計算應該放在...
重構Step4: Move點數計算<br />動機: 類似於金額計算,點數計算功能應該要屬於 Rental 比較洽當<br />作法:<br />將Customer.statement() 內的點數計算程式碼搬到 Rental <br />Ex...
重構Step5: 移除 Temp Variables<br />動機: Temp Variable 可能會是個問題,助長程式碼過長<br />由於變數只是暫時的,所以能見度只在這個程式區塊。若在其他區塊也想要使用這個 Temp Variable...
關於 Performance 這件事…<br />Performance 應該是在最佳化效能時再來擔心,在重構的階段先不用擔心這件事情<br />Performance 應該是透過工具來檢測,肉眼是很難看出來的<br />回想重構的目的…<br...
重構後…<br />現在可以透過這些 Query Methods 來取得資訊,而不需研究細節<br />
重構Step6: Move金額計算、點數計算<br />動機:Rental 必須取得 Movie 才能運算金額。(物件應該在自己的資料上運算邏輯,而不是拿別人的資料來運算)<br />考慮該邏輯該屬於哪個物件<br />影片的價格邏輯是應該放到...
So Far So Good…<br />寫了幾個新的 Query Methods 可以 reuse<br />程式碼更容易閱讀<br />But! 計算金額的那段 switch 還是<br />不利於將來計費邏輯的變動<br />
考慮繼承…<br />使用 Polymorphism 來取代 switch<br />問題來了,若之後 Movie有其他類似 getCharge() 容易變動的邏輯,就很不利於維護<br />
使用Strategy Pattern<br />Strategy Pattern 封裝了會變動的演算法(Price subclasses),所以不會影響使用演算法的程式(Movie)<br />
重構Step7: Replace Type Code with State/Strategy<br />作法(1/2):<br />Replace Type Code with State/Strategy: <br />將 type code...
重構Step8: Replace Conditional with Polymorphism<br />作法(2/2):<br />Replace Conditional with Polymorphism:<br />在 sub Price ...
Final Class Diagram<br />
重構後的好處<br />讓程式碼更容易閱讀<br />Rename Method<br />Extract Method<br />Move Method<br />新的 Query Methods 可以 reuse<br />Replace ...
Thank You!<br />Slide & Example Code URL:<br />xxxx<br />
Upcoming SlideShare
Loading in …5
×

重構—改善既有程式的設計(chapter 1)

5,444 views

Published on

Published in: Education, Technology
  • Be the first to comment

重構—改善既有程式的設計(chapter 1)

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

×