Your SlideShare is downloading. ×
重構—改善既有程式的設計(chapter 1)
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

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

3,405
views

Published on

Published in: Education, Technology

0 Comments
10 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,405
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
81
Comments
0
Likes
10
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

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