Hubert ChanRefactoring – chap 8-2
8.9 Replace Magic Number with Symbolic ConstantMagic Number 特性具有特殊意義,卻又不能明確表現出其意義。可讀性差 / 不明確 / 牽一髮動全身解決方案以 Constant 取代Magic Numberdouble 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;
8.9 Replace Magic Number with Symbolic ConstantRefactoring 時機Magic Number 是 Type CodeReplace Type Code With Class陣列長度使用 Array.length()關鍵字conststaticfinal
8.10 Encapsulate Fieldclass A {  public String _name;}class A {private String _name; public String getName() {return _name;} public void setName(String arg) {_name = arg;}}
8.10 Encapsulate Field動機封裝 Encapsulation資料隱藏 Data Hidingpublic 欄位:資料可以被修改、存取,但是擁有的物件卻無法控制其行為改變存取資料為(Objective-C 2.0 property)readonlyreadwriteassigncopy
8.11 Encapsulate Collection目的傳回 Collection 的一個 readonly view提供 add/remove 元素的 function
8.11 Encapsulate Collection動機Getter 不該傳回 collection 本身暴露過多實作細節使用者可以存取 getter 拿到的 collection使用 add/remove 函式添加元素使用者不用管底層使用哪種 Collection(List, Array, Vector)
8.11 Encapsulate Collectionclass 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,非複製
8.11 Encapsulate CollectionPerson 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
8.11 Encapsulate CollectionRefactoring 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
8.11 Encapsulate CollectionRefactoring 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));
8.11 Encapsulate CollectionRefactoring for JAVA 1.2 – 不該讓使用者透過 getter 修改物件內的 collectionGetter 回傳 unmodified setkent.getCourses().add(new Course("C Programming", false));Class Person {  public Set getCourse() {return Collections.unmodifiableSet(_courses);}}//回傳 unmodifiable的 Set
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;  }}
8.12 Replace Record with Data Class手法把傳統的 record structure 轉為 data class使用 getter/setterclass Person {  String getName() {   return _name;  }  String setName(String arg) {   _name = arg;  } private String _name;}class PersonRecord {  public char[] name;}
8.13 Replace Type Code with Class目的Type Code 沒有型別檢驗Type Code => Class,factory class 可以做型別檢驗
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,不是特別的型別
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
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);    }  }
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
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);
8.14 Replace Type Code with Subclasses使用時機有一個 type code 會影響 class 的行為為了 Replace Conditional with Polymorphism 鋪路彰顯「特定類別的行為」
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
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
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邏輯在各類別中
8.14 Replace Type Code with SubclassesRefactoring 之後把只與各個類別相關的函式放在類別中Push Down MethodPush Down Field
8.15 Replace Type Code with State/Strategy使用時機有一個 type code 他會影響 class 行為無法使用 subclassing,例如 type 可變手法以 state object 取代 type code比較 Replace Type Code with Class
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
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 進行運算
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
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//搬出去?
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
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:...        }    }
8.16 Replace Subclass with Fields使用時機Subclass 的 function 是 constant method可以透過回傳一個 field 來取代 constant method
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
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();    }
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
8.16 Replace Subclass with Fields移除無用的 subclass class Person    static Person createMale(){        return new Person(true, 'M');    }//以 factory pattern 抽換類別

重構—改善既有程式的設計(chapter 8)part 2

  • 1.
  • 2.
    8.9 Replace MagicNumber with Symbolic ConstantMagic Number 特性具有特殊意義,卻又不能明確表現出其意義。可讀性差 / 不明確 / 牽一髮動全身解決方案以 Constant 取代Magic Numberdouble 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 MagicNumber with Symbolic ConstantRefactoring 時機Magic Number 是 Type CodeReplace Type Code With Class陣列長度使用 Array.length()關鍵字conststaticfinal
  • 4.
    8.10 Encapsulate FieldclassA { 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 Hidingpublic 欄位:資料可以被修改、存取,但是擁有的物件卻無法控制其行為改變存取資料為(Objective-C 2.0 property)readonlyreadwriteassigncopy
  • 6.
    8.11 Encapsulate Collection目的傳回Collection 的一個 readonly view提供 add/remove 元素的 function
  • 7.
    8.11 Encapsulate Collection動機Getter不該傳回 collection 本身暴露過多實作細節使用者可以存取 getter 拿到的 collection使用 add/remove 函式添加元素使用者不用管底層使用哪種 Collection(List, Array, Vector)
  • 8.
    8.11 Encapsulate CollectionclassCourse { 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 CollectionPersonkent = 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 CollectionRefactoringfor 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 CollectionRefactoringfor 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 CollectionRefactoringfor JAVA 1.2 – 不該讓使用者透過 getter 修改物件內的 collectionGetter 回傳 unmodified setkent.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 Recordwith Data Class手法把傳統的 record structure 轉為 data class使用 getter/setterclass Person { String getName() { return _name; } String setName(String arg) { _name = arg; } private String _name;}class PersonRecord { public char[] name;}
  • 15.
    8.13 Replace TypeCode with Class目的Type Code 沒有型別檢驗Type Code => Class,factory class 可以做型別檢驗
  • 16.
    8.13 Replace TypeCode 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 TypeCode 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 TypeCode 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 TypeCode 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 TypeCode 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 TypeCode with Subclasses使用時機有一個 type code 會影響 class 的行為為了 Replace Conditional with Polymorphism 鋪路彰顯「特定類別的行為」
  • 22.
    8.14 Replace TypeCode 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 TypeCode 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 TypeCode 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 TypeCode with SubclassesRefactoring 之後把只與各個類別相關的函式放在類別中Push Down MethodPush Down Field
  • 26.
    8.15 Replace TypeCode with State/Strategy使用時機有一個 type code 他會影響 class 行為無法使用 subclassing,例如 type 可變手法以 state object 取代 type code比較 Replace Type Code with Class
  • 27.
    8.15 Replace TypeCode 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 TypeCode 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 TypeCode 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 TypeCode 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 TypeCode 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 TypeCode 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 Subclasswith Fields使用時機Subclass 的 function 是 constant method可以透過回傳一個 field 來取代 constant method
  • 34.
    8.16 Replace Subclasswith 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 Subclasswith 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 Subclasswith 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 Subclasswith Fields移除無用的 subclass class Person static Person createMale(){ return new Person(true, 'M'); }//以 factory pattern 抽換類別