Upcoming SlideShare
×

# 重構—改善既有程式的設計（chapter 9）

1,035 views
952 views

Published on

1 Like
Statistics
Notes
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

Views
Total views
1,035
On SlideShare
0
From Embeds
0
Number of Embeds
18
Actions
Shares
0
59
0
Likes
1
Embeds 0
No embeds

No notes for slide

### 重構—改善既有程式的設計（chapter 9）

1. 1. Refactoring<br />Chapter 9<br />Simplifying Conditional Expressions<br />
2. 2. Read this<br />and then read this<br />Decompose Conditional<br />if (date.before (SUMMER_START) || date.after(SUMMER_END))<br /> charge = quantity * _winterRate + _winterServiceCharge;<br />else charge = quantity * _summerRate;<br />if (notSummer(date))<br /> charge = winterCharge(quantity);<br />else<br /> charge = summerCharge (quantity);<br />
3. 3. Consolidate Conditional Expression<br />Before:<br />After:<br />double disabilityAmount() {<br /> if (_seniority < 2) return 0;<br /> if (_monthsDisabled > 12) return 0;<br /> if (_isPartTime) return 0;<br /> // ...<br />}<br />double disabilityAmount() {<br /> if (isNotEligableForDisability()) return 0;<br /> // ...<br />}<br />
4. 4. Before<br />if (isSpecialDeal()) {<br />doSomePreparation();<br />doNonRelatedWork();<br /> total = price * 0.95;<br /> send();<br />doSomeCleanUp();<br />}<br />else {<br />doSomePreparation();<br /> total = price * 0.98;<br /> send();<br />doNonRelatedWork();<br />doSomeCleanUp();<br />}<br />Consolidate Duplicate Conditional Fragments<br />
5. 5. Consolidate Duplicate Conditional Fragments<br />After<br />doNonRelatedWork();<br />doSomePreparation();<br />if (isSpecialDeal())<br /> total = price * 0.95;<br />else<br /> total = price * 0.98;<br />send();<br />doSomeCleanUp();<br />
6. 6. Remove Control Flag<br />Trace this<br />void checkSecurity(String[] people) {<br />boolean found = false;<br /> for (int i = 0; i < people.length; i++) {<br /> if (! found) {<br /> if (people[i].equals ("Don")){<br />sendAlert();<br /> found = true;<br /> }<br /> if (people[i].equals ("John")){<br />sendAlert();<br /> found = true;<br /> }<br /> }<br /> }<br />}<br />
7. 7. Remove Control Flag<br />○use return, break, continue …<br />✕set a flag and check somewhere later<br />
8. 8. Alternative 1:<br />Remove Control Flag<br />void checkSecurity(String[] people) {<br /> for (int i = 0; i < people.length; i++) {<br /> if (people[i].equals ("Don")){<br />sendAlert();<br /> break;<br /> }<br /> if (people[i].equals ("John")){<br />sendAlert();<br /> break;<br /> }<br /> }<br />}<br />
9. 9. Alternative 2:<br />Remove Control Flag<br />void checkSecurity(String[] people) {<br /> String found = foundMiscreant(people);<br />someLaterCode(found);<br />}<br />String foundMiscreant(String[] people) {<br /> for (int i = 0; i < people.length; i++) {<br /> if (people[i].equals ("Don")){<br />sendAlert();<br /> return "Don";<br /> }<br /> if (people[i].equals ("John")){<br />sendAlert();<br /> return "John";<br /> }<br /> }<br /> return "";<br />}<br />
10. 10. Before<br />Replace Nested Conditional with Guard Clauses<br />double getPayAmount() {<br /> double result;<br /> if (_isDead) {<br /> result = deadAmount();<br /> } else {<br /> if (_isSeparated) {<br /> result = separatedAmount();<br /> } else {<br /> if (_isRetired) result = retiredAmount();<br /> else result = normalPayAmount();<br /> }<br /> }<br /> return result;<br />}<br />
11. 11. After<br />Replace Nested Conditional with Guard Clauses<br />double getPayAmount() {<br /> if (_isDead)<br /> return deadAmount();<br /> if (_isSeparated)<br /> return separatedAmount();<br /> if (_isRetired)<br /> return retiredAmount();<br /> return normalPayAmount();<br />};<br />
12. 12. Mind implicit semantics<br /><ul><li>Guard clause</li></ul>-> (somehow) exceptional condition<br /><ul><li>if else</li></ul>-> two equivalent normal conditions<br />Replace Nested Conditional with Guard Clauses<br />
13. 13. Replace Nested Conditional with Guard Clauses<br />However, you might have seen this …<br />if (aquireResource1()) {<br /> if (aquireResource2()) {<br />if (aquireResource3()){<br /> if (aquireResource4()) {<br /> doRealWork();<br />releaseResource4();<br /> }<br /> releaseResource3();<br /> }<br /> releaseResource2();<br /> }<br />releaseResource1();<br />}<br />
14. 14. Replace Nested Conditional with Guard Clauses<br />or this …<br />if (false == aquireResource1())<br />return;<br />if (false == aquireResource2()) {<br />releaseResource1();<br /> return;<br />}<br />if (false == aquireResource3()){<br />releaseResource2();<br />releaseResource1();<br />return;<br />}<br />doRealWork();<br />releaseResource3();<br />releaseResource2();<br />releaseResource1();<br />return;<br />
15. 15. Before<br />Replace Conditional with Polymorphism<br />class Employee...<br /> int payAmount(Employee) {<br /> switch (getType()) {<br /> case EmployeeType.ENGINEER:<br /> return _monthlySalary;<br /> case EmployeeType.SALESMAN:<br /> return _monthlySalary + _commission;<br /> case EmployeeType.MANAGER:<br /> return _monthlySalary + _bonus;<br /> default:<br /> throw new RuntimeException("Incorrect Employee");<br /> }<br /> }<br />
16. 16. ！<br />○Polymorphism might be better, usually<br />Replace Conditional with Polymorphism<br />switch case<br />clauses<br />
17. 17. class Employee...<br />intpayAmount() {<br /> return _type.payAmount(this);<br />}<br />class Salesman...<br />intpayAmount(Employee emp) {<br /> return emp.getMonthlySalary() + emp.getCommission();<br /> }<br />class Manager...<br />intpayAmount(Employee emp) {<br /> return emp.getMonthlySalary() + emp.getBonus();<br /> }<br />After<br />Replace Conditional with Polymorphism<br />
18. 18. <ul><li>Same with Replace Conditional with Polymorphism
19. 19. deal with special cases extensively checked
20. 20. check type -> check NULL
21. 21. Before:</li></ul>Introduce Null Object<br />class Customer...<br /> public String getName() {...}<br /> public BillingPlangetPlan() {...}<br /> public PaymentHistorygetHistory() {...}<br />if (customer == null) plan = BillingPlan.basic();<br />else plan = customer.getPlan();<br />
22. 22. After<br />Introduce Null Object<br />plan = customer.getPlan();<br />class NullCustomer...<br /> public BillingPlangetPlan(){<br /> return BillingPlan.basic();<br /> }<br />
23. 23. Write your assumption explicitly and clearly<br />Introduce Assertion<br /> double getExpenseLimit() {<br />// should have either expense limit or a primary project<br /> return (_expenseLimit != NULL_EXPENSE) ?<br /> _expenseLimit:<br /> _primaryProject.getMemberExpenseLimit();<br />}<br /> double getExpenseLimit() {<br />Assert.isTrue (_expenseLimit != NULL_EXPENSE || _primaryProject != null);<br /> return (_expenseLimit != NULL_EXPENSE) ?<br /> _expenseLimit:<br /> _primaryProject.getMemberExpenseLimit();<br />}<br />
24. 24. Do NOT overuse<br />If code does work without the assertion, remove it.<br />Introduce Assertion<br />
25. 25. Notes<br />Save brain power<br />Break/prune eye-tracing as early as possible<br />Don’t Repeat Yourself<br />