Replace Type Code With Class和Replace Type Code With Subclass和Replace Type Code With State/Strategy

  周末闲来写写看书总结,今天写《重构》中的3个重要手法,分别是Replace Type Code With Class、Replace Type Code With Subclass和Replace Type Code With State/Strategy。

  1、Replace Type Code With Class

  重构前的主体代码如下:

 1 package nelson;
 2 
 3 public class Person {
 4     
 5     public static final int O = 0;
 6     public static final int A = 1;
 7     public static final int B = 2;
 8     public static final int AB = 3;
 9     
10     private int _bloodGroup;
11     
12     public Person(int bloodGroup){
13         _bloodGroup = bloodGroup;
14     }
15     
16     public void setBloodGroup(int arg){
17         _bloodGroup = arg;
18     }
19     
20     public int getBloodGroup(){
21         return _bloodGroup;
22     }
23 }
View Code

  重构前的应用代码如下:

 1 package nelson;
 2 
 3 public class HelloJava {
 4 
 5     public static void main(String[] args) {
 6             
 7         System.out.println("Hello,Java");
 8         
 9         Person xiaoming = new Person(Person.A);  //小明A型血
10         
11         System.out.println("小明的血型是:"+xiaoming.getBloodGroup());
12     }
13 
14 }
View Code

  重构前的代码有什么问题呢,在Person中使用public static final修饰了几个变量,这几个变量是血型的类型值。在new对象或者setBloodGroup时可以将此作为参数传入。问题有如下:在New对象或者setBloodGroup时,可以传入其他参数,另外Person.A这样的形式不能直观反映A的含义,如果取为BloodGroup.A--血型,血型中的A型血,是不是更直接明了。

  重构后的主体代码:

 1 package nelson;
 2 
 3 public class Person {
 4     
 5     private BloodGroup _bloodGroup;
 6     
 7     public Person(BloodGroup bloodGroup){
 8         _bloodGroup = bloodGroup;
 9     }
10     
11     public void setBloodGroup(BloodGroup arg){
12         _bloodGroup = arg;
13     }
14     
15     public BloodGroup getBloodGroup(){
16         return _bloodGroup;
17     }
18 }
19 
20 class BloodGroup
21 {
22     public static final BloodGroup O = new BloodGroup(0);
23     public static final BloodGroup A = new BloodGroup(1);
24     public static final BloodGroup B = new BloodGroup(2);
25     public static final BloodGroup AB = new BloodGroup(3);
26     public static final BloodGroup[] _values = {O,A,B,AB};
27     
28     private final int _code;
29     
30     private BloodGroup(int code)
31     {
32         _code = code;
33     }
34     
35     public int getCode()
36     {
37         return _code;
38     }
39     
40     public BloodGroup getBloodGroup(int arg)
41     {
42         return _values[arg];
43     }
44 }
View Code

  重构后的应用代码:

 1 package nelson;
 2 
 3 public class HelloJava {
 4 
 5     public static void main(String[] args) {
 6             
 7         System.out.println("Hello,Java");
 8         
 9         Person xiaoming = new Person(BloodGroup.A);  //小明A型血
10         
11         System.out.println("小明的血型是:"+xiaoming.getBloodGroup().getCode());
12     }
13 }
View Code

       重构后比重构前有哪些优势呢?

       new Person时需要传入BloodGroup类型参数而不再是int类型参数,这样就有参数类型检查了。参数为BloodGroup.A这样就更容易理解了。

       其实以上就是用class来完成枚举enum的实现了。

  2、Replace Type Code With Subclass

     重构前的主体类Employee,代表员工,有3中类型(Type)。

 1 package nelson;
 2 
 3 public class Employee {
 4     
 5     private int _type;   //员工类型
 6     public static final int ENGINEER = 0;
 7     public static final int SALEMAN = 1;
 8     public static final int MANAGER = 2;
 9     
10     public Employee(int type)
11     {
12         _type = type;
13     }
14     
15     public int getType()
16     {
17         return _type;
18     }
19 }
View Code

        重构前的应用代码:

 1 package nelson;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class HelloJava {
 7 
 8     public static void main(String[] args) {
 9             
10         System.out.println("Hello,Java");
11         
12         Employee xiaotang = new Employee(Employee.ENGINEER);   //小唐是工程师
13         Employee xiaoFang = new Employee(Employee.SALEMAN);    //小方是销售
14         Employee laozhou = new Employee(Employee.MANAGER);     //老周是经理
15         
16         List<Employee> allStaffs = new ArrayList<Employee>();  //所有员工
17         
18         allStaffs.add(xiaotang);
19         allStaffs.add(xiaoFang);
20         allStaffs.add(laozhou);
21         
22         //为所有员工发年终奖,喜闻乐见
23         System.out.println("\n为所有员工发年终奖");
24         for(Employee staff : allStaffs)
25         {
26             switch(staff.getType())
27             {
28             case Employee.ENGINEER: System.out.println("多发一个月工资");break;
29             case Employee.SALEMAN: System.out.println("多发1.5个月工资");break;
30             case Employee.MANAGER:System.out.println("多发2个月工资");break;
31             default:break;
32             }
33         }
34         
35         System.out.println("\n确定所有员工的春节放假时间");
36         for(Employee staff : allStaffs)
37         {
38             switch(staff.getType())
39             {
40             case Employee.ENGINEER: System.out.println("休息7天");break;
41             case Employee.SALEMAN: System.out.println("休息10天");break;
42             case Employee.MANAGER:System.out.println("休息5天");break;
43             default:break;
44             }
45         }
46     }
47 }
View Code

  这里有什么问题呢,看起来逻辑也是很清晰的。问题在于,应用代码中需要不断地判断员工类型。也可以将发放年终奖做成一个函数定义在Employee中,如PaidAnnualBonus(),将春节休假时间做成一个函数定义在Employee中,如SpringFestivalVacationTime(),可以确定是这两个函数里依然会对员工类型做判断。

  重构后的主体类:

 1 package nelson;
 2 
 3 public abstract class Employee {
 4     
 5     public static final int ENGINEER = 0;
 6     public static final int SALEMAN = 1;
 7     public static final int MANAGER = 2;
 8     
 9     public Employee()
10     {
11     }
12     
13     public static Employee Create(int type)
14     {
15         switch(type)
16         {
17         case ENGINEER: return new Engineer();
18         case SALEMAN: return new Salesman();
19         case MANAGER: return new Manager();
20         default:throw new IllegalArgumentException("Incorrect type code value");
21         }
22     }
23     
24     abstract int getType();
25     abstract void PaidAnnualBonus();  //发年终奖
26     abstract void SpringFestivalVacationTime(); //春节放假时间
27 }
28 
29 class Engineer extends Employee
30 {
31 
32     public Engineer() {
33         super();
34     }
35     
36     public int getType()
37     {
38         return Employee.ENGINEER;
39     }
40     
41     public void PaidAnnualBonus()
42     {
43         System.out.println("我是工程师,我年终奖多发一个月工资");
44     }
45     
46     public void SpringFestivalVacationTime()
47     {
48         System.out.println("我是工程师,我春节放假7天");
49     }
50 }
51 
52 class Salesman extends Employee
53 {
54 
55     public Salesman() {
56         super();
57     }
58     
59     public int getType()
60     {
61         return Employee.SALEMAN;
62     }
63     
64     public void PaidAnnualBonus()
65     {
66         System.out.println("我是销售员,我年终奖多发一个半月工资");
67     }
68     
69     public void SpringFestivalVacationTime()
70     {
71         System.out.println("我是销售员,我春节放假10天");
72     }
73 }
74 
75 class Manager extends Employee
76 {
77 
78     public Manager() {
79         super();
80     }
81     
82     public int getType()
83     {
84         return Employee.MANAGER;
85     }
86     
87     public void PaidAnnualBonus()
88     {
89         System.out.println("我是经理,我年终奖多发两个月工资");
90     }
91     
92     public void SpringFestivalVacationTime()
93     {
94         System.out.println("我是经理,我春节放假5天");
95     }
96 }
View Code

  重构后的应用代码:

 1 package nelson;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class HelloJava {
 7 
 8     public static void main(String[] args) {
 9             
10         System.out.println("Hello,Java");
11         
12         Employee xiaotang = Employee.Create(Employee.ENGINEER);   //小唐是工程师
13         Employee xiaoFang = Employee.Create(Employee.SALEMAN);    //小方是销售
14         Employee laozhou = Employee.Create(Employee.MANAGER);     //老周是经理
15         
16         List<Employee> allStaffs = new ArrayList<Employee>();  //所有员工
17         
18         allStaffs.add(xiaotang);
19         allStaffs.add(xiaoFang);
20         allStaffs.add(laozhou);
21         
22         //为所有员工发年终奖,喜闻乐见
23         System.out.println("\n为所有员工发年终奖");
24         
25         for(Employee staff : allStaffs)
26         {
27             staff.PaidAnnualBonus();
28         }
29         
30         System.out.println("\n确定所有员工的春节放假时间");
31         for(Employee staff : allStaffs)
32         {
33             staff.SpringFestivalVacationTime();
34         }
35     }
36 }
View Code

  重构后的应用代码变得很简洁,利用了Employee的Create函数,应用类根本不用知道Engineer类、Salesman类、Manager类的存在。

  3、Replace Type Code With State/Strategy

  关于这一点的动机,参照《重构》好好理解吧。参见下图:

  Replace Type Code With Subclass和Replayce Type Code With State/Strategy很类似,Replace-State/Strategy更彻底,将type单独列出来作为一个类,并将它作为宿主类的一个参数,好处就是上图中介绍的,“类型吗的值在对象生命周期中发生变化”和“其他原因使得宿主类不能被继承”。这两句话比较难懂,但考虑这样一个问题就好理解了。上面的Replace-Subclass例子中,如果一个对象被定义为了工程师,现在他得到了提升变为了经理,上面的代码就很难做到。后面“其他原因使得宿主类不能被继承”这里的其他原因确实还没想好(囧)。

  重构前的代码跟上面Replace Type Code With Subclass一样,也就是Replace Type Code With State/Strategy是Repace Type Code With Subclass的升级版。

  重构后的主体代码如下:

  1 package nelson;
  2 
  3 public class Employee {
  4     
  5     EmployeeType employeeType;
  6     
  7     public Employee()
  8     {
  9     }
 10     
 11     public void setType(int arg)
 12     {
 13         employeeType = EmployeeType.Create(arg);
 14     }
 15     
 16     public void PaidAnnualBonus()
 17     {
 18         employeeType.PaidAnnualBonus();
 19     }
 20     
 21     public void SpringFestivalVacationTime()
 22     {
 23         employeeType.SpringFestivalVacationTime();
 24     }
 25 }
 26 
 27 abstract class EmployeeType
 28 {
 29     public static final int ENGINEER = 0;
 30     public static final int SALEMAN = 1;
 31     public static final int MANAGER = 2;
 32     
 33     abstract int getType();
 34     abstract void PaidAnnualBonus();  //发年终奖
 35     abstract void SpringFestivalVacationTime(); //春节放假时间
 36     
 37     public static EmployeeType Create(int type)
 38     {
 39         switch(type)
 40         {
 41         case ENGINEER: return new Engineer();
 42         case SALEMAN: return new Salesman();
 43         case MANAGER: return new Manager();
 44         default:throw new IllegalArgumentException("Incorrect type code value");
 45         }
 46     }
 47 }
 48 
 49 class Engineer extends EmployeeType
 50 {
 51 
 52     public Engineer() {
 53         super();
 54     }
 55     
 56     public int getType()
 57     {
 58         return EmployeeType.ENGINEER;
 59     }
 60     
 61     public void PaidAnnualBonus()
 62     {
 63             System.out.println("我是工程师,我年终奖多发一个月工资");
 64     }
 65     
 66     public void SpringFestivalVacationTime()
 67     {
 68         System.out.println("我是工程师,我春节放假7天");
 69     }
 70 }
 71 
 72 class Salesman extends EmployeeType
 73 {
 74 
 75     public Salesman() {
 76         super();
 77     }
 78     
 79     public int getType()
 80     {
 81         return EmployeeType.SALEMAN;
 82     }
 83     
 84     public void PaidAnnualBonus()
 85     {
 86             System.out.println("我是销售员,我年终奖多发一个半月工资");
 87     }
 88     
 89     public void SpringFestivalVacationTime()
 90     {
 91         System.out.println("我是销售员,我春节放假10天");
 92     }
 93 }
 94 
 95 class Manager extends EmployeeType
 96 {
 97     public Manager() {
 98         super();
 99     }
100     
101     public int getType()
102     {
103         return EmployeeType.MANAGER;
104     }
105     
106     public void PaidAnnualBonus()
107     {
108             System.out.println("我是经理,我年终奖多发两个月工资");
109     }
110     
111     public void SpringFestivalVacationTime()
112     {
113         System.out.println("我是经理,我春节放假5天");
114     }
115 }
View Code

  重构后的应用代码如下:

 1 package nelson;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class HelloJava {
 7 
 8     public static void main(String[] args) {
 9             
10         System.out.println("Hello,Java");
11         
12         Employee xiaotang = new Employee();
13         xiaotang.setType(EmployeeType.ENGINEER);   //小唐是工程师
14         Employee xiaoFang = new Employee();
15         xiaoFang.setType(EmployeeType.SALEMAN);    //小方是销售
16         Employee laozhou = new Employee();
17         laozhou.setType(EmployeeType.MANAGER);     //老周是经理
18         
19         List<Employee> allStaffs = new ArrayList<Employee>();  //所有员工
20         
21         allStaffs.add(xiaotang);
22         allStaffs.add(xiaoFang);
23         allStaffs.add(laozhou);
24         
25         //为所有员工发年终奖,喜闻乐见
26         System.out.println("\n为所有员工发年终奖");
27         
28         for(Employee staff : allStaffs)
29         {
30             staff.PaidAnnualBonus();
31         }
32         
33         System.out.println("\n确定所有员工的春节放假时间");
34         for(Employee staff : allStaffs)
35         {
36             staff.SpringFestivalVacationTime();
37         }
38     }
39 }
View Code

  重构后的好处就像上面的两条动机那样,Employee类现在就可以动态改变员工类型属性了,还有就是Employee类也可以被方便的继承而不受约束。

posted @ 2017-10-15 13:06  kanite  阅读(878)  评论(0编辑  收藏  举报