2. 8.9 Replace Magic Number with Symbolic Constant Magic Number 特性 具有特殊意義,卻又不能明確表現出其意義。 可讀性差 / 不明確 / 牽一髮動全身 解決方案 以 Constant 取代Magic Number double potentialEngery(double mass, double height) { return mass * 9.81 * height } double potentialEngery(double mass, double height) { return mass * GRAVITATIONAL_CONSTANT * height } static final double GRAVITATIONAL_CONTSTANT = 9.81;
3. 8.9 Replace Magic Number with Symbolic Constant Refactoring 時機 Magic Number 是 Type Code Replace Type Code With Class 陣列長度 使用 Array.length() 關鍵字 const static final
4. 8.10 Encapsulate Field class A { public String _name; } class A { private String _name; public String getName() {return _name;} public void setName(String arg) {_name = arg;} }
5. 8.10 Encapsulate Field 動機 封裝 Encapsulation 資料隱藏 Data Hiding public 欄位:資料可以被修改、存取,但是擁有的物件卻無法控制其行為 改變存取資料為(Objective-C 2.0 property) readonly readwrite assign copy
8. 8.11 Encapsulate Collection class Course { public Course (String name, booleanisAdvanced) {} public booleanisAdvanced() {} } Class Person { public Set getCourses() { return _courses; } public void setCourses(Set arg) { _courses = arg; } private Set _courses; } //使用者直接操作 data //直接 assign,非複製
9. 8.11 Encapsulate Collection Person kent = new Person(); Set s = new HashSet(); s.add(new Course("Smalltalk Programming", false)); s.add(new Course("Appreciating Single Malts", true)); kent.setCourses(s); Course refact = new Course("Refactoring", true); kent.getCourses().add(refact); kent.getCourses().remove(refact); //直接 assign,非複製 //使用者直接操作 data //使用者直接操作 data
10. 8.11 Encapsulate Collection Refactoring for JAVA 1.2 – 使用 add/remove 函式 Class Person { public void addCourse(Course arg) { _courses.add(arg); } public void removeCourse(Course arg) _courses.remove(arg); } public void initializeCourses(Set arg) { Assert.isTrue(_courses.isEmpty()); _courses.addAll(arg); } } //內部的 _courses != arg
11. 8.11 Encapsulate Collection Refactoring for JAVA 1.2 – 使用 add/remove 函式 Person kent = new Person(); Set s = new HashSet(); s.add(new Course("Smalltalk Programming", false)); s.add(new Course("Appreciating Single Malts", true)); kent.setCourses(s); Person kent = new Person(); kent.addCourse(new Course("Smalltalk Programming", false)); kent.addCourse(new Course("Appreciating Single Malts", true));
12. 8.11 Encapsulate Collection Refactoring for JAVA 1.2 – 不該讓使用者透過 getter 修改物件內的 collection Getter 回傳 unmodified set kent.getCourses().add(new Course("C Programming", false)); Class Person { public Set getCourse() { return Collections.unmodifiableSet(_courses);} } //回傳 unmodifiable的 Set
13. 8.11 Encapsulate Collection 將行為移至 class 內 Move Method 更好維護 class Person { intnumberOfAdvancedCourses(Person person) { Iterator iter = person.getCourses().iterator(); int count = 0; while (iter.hasNext()) { Course each = (Course) iter.next(); if (each.isAdvanced()) count++; } return count; } }
14. 8.12 Replace Record with Data Class 手法 把傳統的 record structure 轉為 data class 使用 getter/setter class Person { String getName() { return _name; } String setName(String arg) { _name = arg; } private String _name; } class PersonRecord { public char[] name; }
15. 8.13 Replace Type Code with Class 目的 Type Code 沒有型別檢驗 Type Code => Class,factory class 可以做型別檢驗
16. 8.13 Replace Type Code with Class 目標是 Person 中的 Type Code class Person { public static final int O = 0; public static final int A = 1; public static final int B = 2; public static final int AB = 3; private int _bloodGroup; public Person (intbloodGroup) { _bloodGroup = bloodGroup; } public void setBloodGroup(intarg) { _bloodGroup = arg; } public intgetBloodGroup() { return _bloodGroup; } } //型別是 int,不是特別的型別
17. 8.13 Replace Type Code with Class 把 Person 中 Type Code 換成 BloodGroup Class class BloodGroup { public static final BloodGroup O = new BloodGroup(0); public static final BloodGroup A = new BloodGroup(1); public static final BloodGroup B = new BloodGroup(2); public static final BloodGroup AB = new BloodGroup(3); private static final BloodGroup[] _values = {O, A, B, AB}; private finalint _code; privateBloodGroup (int code ) { _code = code; } public intgetCode() { return _code; } public static BloodGroup code(intarg) { return _values[arg]; } } //class instance 而非 Type Code //constructor 是 private
18. 8.13 Replace Type Code with Class 使用 Class 並維持原本的對外介面 class Person { public static final int O = BloodGroup.O.getCode(); public static final int A = BloodGroup.A.getCode(); public static final int B = BloodGroup.B.getCode(); public static final int AB = BloodGroup.AB.getCode(); private BloodGroup _bloodGroup; public Person (intbloodGroup) { _bloodGroup = BloodGroup.code(bloodGroup); } public intgetBloodGroup() { return _bloodGroup.getCode(); } public void setBloodGroup(intarg) { _bloodGroup = BloodGroup.code (arg); } }
19. 8.13 Replace Type Code with Class 在 Constructor 使用 Class 做參數 為新的介面加入 setter/getter class Person { public Person (BloodGroupbloodGroup) { _bloodGroup = bloodGroup; } public void setBloodGroup(BloodGrouparg) { _bloodGroup = arg; } public BloodGroupgetBloodGroup() { return _bloodGroup; } } //使用 BloodGroup // Setter // Getter
20. 8.13 Replace Type Code with Class 使用者端的改變 Person thePerson = new Person(PersonA); thePerson.getBloodGroupCode(); thePerson.setBloodGroup(Person.AB); Person thePerson = new Person(BloodGroup.A); thePerson.getBloodGroup(); thePerson.setBloodGroup(BloodGroup.AB);
21. 8.14 Replace Type Code with Subclasses 使用時機 有一個 type code 會影響 class 的行為 為了 Replace Conditional with Polymorphism 鋪路 彰顯「特定類別的行為」
22. 8.14 Replace Type Code with Subclasses 目標 class 具有會影響行為的 type code class Employee{ private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; Employee (int type) { _type = type; } } //Type Code
23. 8.14 Replace Type Code with Subclasses 在 Base Class 將行為的 function 抽出來 getType() class Employee{ abstractintgetType(); } class Engineer extends Employee { intgetType() { return Employee.ENGINEER; } } //abstract 強制 subclass 需要 implement // implement
24. 8.14 Replace Type Code with Subclasses 修改 factory class class Employee { static Employee create(int type) { switch (type) { case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: ... } } //getType邏輯在各類別中
25. 8.14 Replace Type Code with Subclasses Refactoring 之後 把只與各個類別相關的函式放在類別中 Push Down Method Push Down Field
26. 8.15 Replace Type Code with State/Strategy 使用時機 有一個 type code 他會影響 class 行為 無法使用 subclassing,例如 type 可變 手法 以 state object 取代 type code 比較 Replace Type Code with Class
27. 8.15 Replace Type Code with State/Strategy 目標:將 type code 以 state object 取代 class Employee{ private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; Employee (int type) { _type = type; } } //Type Code
28. 8.15 Replace Type Code with State/Strategy 觀察:如何使用 type code class Employee{ intpayAmount() { switch (_type) { case ENGINEER: return _monthlySalary; case SALESMAN: return _monthlySalary + _commission; case MANAGER: return _monthlySalary + _bonus; default: throw new RuntimeException("Incorrect Employee"); } } } //依照 Type Code 進行運算
29. 8.15 Replace Type Code with State/Strategy 設計 state object / 共有 state 的部份 使用 abstract class / abstract function abstract class EmployeeType { abstract intgetTypeCode(); } class Engineer extends EmployeeType { intgetTypeCode () { return Employee.ENGINEER; } } //abstract function //subclass implementation
30. 8.15 Replace Type Code with State/Strategy 改變 state 時,改變 state object 的 class class Employee... private EmployeeType _type; void setType(intarg) { switch (arg) { case ENGINEER: _type = new Engineer(); break; case SALESMAN: _type = new Salesman(); break; case MANAGER: _type = new Manager(); break; default: ... } } //很像 factory pattern //搬出去?
31. 8.15 Replace Type Code with State/Strategy 將 factory method 放到 state object class Employee... void setType(intarg) { _type = EmployeeType.newType(arg); } class EmployeeType... static EmployeeTypenewType(int code) { switch (code) { case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: ... } } static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; //setter 使用 state class 的 factory method // state class 的 factory method
32. 8.15 Replace Type Code with State/Strategy 以 state object 的 state 進行運算 接著可以用 Replace Conditional with Polymorphism class Employee... intpayAmount() { switch (getType()) { case EmployeeType.ENGINEER: return _monthlySalary; case EmployeeType.SALESMAN: return _monthlySalary + _commission; case EmployeeType.MANAGER: return _monthlySalary + _bonus; default: ... } }
33. 8.16 Replace Subclass with Fields 使用時機 Subclass 的 function 是 constant method 可以透過回傳一個 field 來取代 constant method
34. 8.16 Replace Subclass with Fields 觀察 abstract method 都只是回傳 hard-code 資料 abstract class Person { abstract booleanisMale(); abstract char getCode(); ... class Male extends Person { booleanisMale() { return true; } char getCode() { return 'M'; } } //return hard-code //return hard-code
35. 8.16 Replace Subclass with Fields 以 Factory Method 隱藏 subclass 類別 設定 subclass 的 constructor 為 private 未來可以移除 subclass class Person... static Person createMale(){ return new Male(); } static Person createFemale() { return new Female(); }
36. 8.16 Replace Subclass with Fields 為 superclass 加上想要取代的 field class Person... protected Person (booleanisMale, char code) { _isMale = isMale; _code = code; } booleanisMale() { return _isMale; } class Male... Male() { super (true, 'M'); } //以 Field 取代 subclass 的 function
37. 8.16 Replace Subclass with Fields 移除無用的 subclass class Person static Person createMale(){ return new Person(true, 'M'); } //以 factory pattern 抽換類別