Replace Type Code with Class (以类取代类型码)
2014-03-24 00:00
435 查看
Summary:类之中有一个数值类型码,但它并不影响类的行为。以一个新的类替换该数值类型码。
动机:
在以C为基础的变成语言中,类型码或枚举值很常见。如果带着一个有意义的符号名,类型码的可读性还是不错的。问题在于,符号名终究是个别名,编译器所看见的、进行类型校验的,还是背后那个数值。任何接受类型码作为参数的函数所期望的实际上是一个数值,无法强制使用符号名。这会大大降低代码的可读性,从而成为bug之源。
如果把那样的数值换成一个类,编译器就可以对这个类进行类型校验。只要为这个类提供工厂函数,你就额可以始终保证只有合法的实例才会被创建出来,而且它们都会被传递给正确的宿主对象。
但是在使用Replace Type Code with Class之前,你应该先考虑类型码的其他替换方式。只有当类型码是纯数据时(也就是类型码不会在switch语句中引起行为变化时),你才能以类来取代它。Java只能以整数作为switch语句的判断依据,不能使用任意类,因此在那种情况下不能够以类替换类型码。更重要的是:任何switch语句都应该运用Replace Conditional with Polymorphism去掉。为了进行那样的重构,你首先运用Replace Type Code with Subclasses或Replace Type Code with State/Strategy,把类型码处理掉。
即使一个类型码不会因其数值的不同而引起行为上的差异,宿主类中的某些行为还是有可能更适合置放于类型码中,因此你还应该留意是否有必要使用Move Method将一两个函数搬过去。
做法:
1.为类型码建立一个类
à这个类需要一个用以记录类型码的字段,其类型应该和类型码相同,并应该由对应的取值函数,此外还应该用一组静态变量保存允许被创建的实例,并以一个静态函数根据原本的类型码返回合适的实例。
2.修改源类实现,让它使用上述新建的类。
à维持原先以类型码为基础的函数接口,但改变静态字段,以新建的类产生代码。然后修改类型码相关函数,让它们也从新建的类中获取类型码。
3.编译,测试。
à此时,新建的类可以对类型码进行运行期检查。
4.对于源类中每一个使用类型码的函数,相应建立一个函数,让新函数使用新建的类。
à你需要建立“以新类实例为自变量”的函数,用以替换原先“直接以类型码为参数”的函数。你还需要建立一个“返回新类实例”的函数,用以替换原先“直接返回类型码”的函数。建立新函数前,你可以使用Rename Method修改原函数名称,明确指出哪些函数仍然使用旧式的类型码,这往往是个明智之举。
5.逐一修改源类用户,让它们使用新接口。
6.每修改一个用户,编译并测试。
à你也可能需要一次性修改多个彼此相关的函数,才能保持这些函数之间的一致性,才能顺利地编译、测试
7.删除使用类型码的旧接口,并删除保存旧类型码的静态变量。
8.编译,测试。
范例:
每个人都拥有四种血型中的一种。我们以Person来表示“人”,以其中的类型码表示“血型”:
首先,我们建立一个新的BloodGroup类,用以表示“血型”,并在这个类实例中保存原本的类型码数值:
然后,把Person中的类型码改为使用BloodGroup类:
现在因为BloodGroup类而拥有了运行期检验能力。为了真正从这些改变中获利,我们还必须修改Person的用户,让它们以BloodGroup对象表示类型码,而不再使用整数。
首先,使用Rename Method修改类型码访问函数的名称,说明当前情况:
然后为Person加入一个新的取值函数,其中使用BloodGroup;
另外,还要建立新的构造函数和设值函数,让它们也使用BloodGroup;
现在,继续处理person用户。此时应该注意,每次只处理一个用户,这样才可以保持小步前进。每个用户需要的修改方式可能不同,这使得修改过程更加棘手。对Person内的静态变量的所有引用点也需要修改。因此,下列代码:
就变成了
原来调用取值函数的代码必须改为调用BloodGroup的取值函数。因此下列代码
变成了
设值函数也一样。因此下列代码
变成了
修改完毕Person的所有用户之后,就可以删掉原本使用整数类型的那些旧的取值函数、构造函数、静态变量和设值函数了。
还可以将BloodGroup中使用整数类型的函数声明为private,因为再也没有人会使用它们了:
动机:
在以C为基础的变成语言中,类型码或枚举值很常见。如果带着一个有意义的符号名,类型码的可读性还是不错的。问题在于,符号名终究是个别名,编译器所看见的、进行类型校验的,还是背后那个数值。任何接受类型码作为参数的函数所期望的实际上是一个数值,无法强制使用符号名。这会大大降低代码的可读性,从而成为bug之源。
如果把那样的数值换成一个类,编译器就可以对这个类进行类型校验。只要为这个类提供工厂函数,你就额可以始终保证只有合法的实例才会被创建出来,而且它们都会被传递给正确的宿主对象。
但是在使用Replace Type Code with Class之前,你应该先考虑类型码的其他替换方式。只有当类型码是纯数据时(也就是类型码不会在switch语句中引起行为变化时),你才能以类来取代它。Java只能以整数作为switch语句的判断依据,不能使用任意类,因此在那种情况下不能够以类替换类型码。更重要的是:任何switch语句都应该运用Replace Conditional with Polymorphism去掉。为了进行那样的重构,你首先运用Replace Type Code with Subclasses或Replace Type Code with State/Strategy,把类型码处理掉。
即使一个类型码不会因其数值的不同而引起行为上的差异,宿主类中的某些行为还是有可能更适合置放于类型码中,因此你还应该留意是否有必要使用Move Method将一两个函数搬过去。
做法:
1.为类型码建立一个类
à这个类需要一个用以记录类型码的字段,其类型应该和类型码相同,并应该由对应的取值函数,此外还应该用一组静态变量保存允许被创建的实例,并以一个静态函数根据原本的类型码返回合适的实例。
2.修改源类实现,让它使用上述新建的类。
à维持原先以类型码为基础的函数接口,但改变静态字段,以新建的类产生代码。然后修改类型码相关函数,让它们也从新建的类中获取类型码。
3.编译,测试。
à此时,新建的类可以对类型码进行运行期检查。
4.对于源类中每一个使用类型码的函数,相应建立一个函数,让新函数使用新建的类。
à你需要建立“以新类实例为自变量”的函数,用以替换原先“直接以类型码为参数”的函数。你还需要建立一个“返回新类实例”的函数,用以替换原先“直接返回类型码”的函数。建立新函数前,你可以使用Rename Method修改原函数名称,明确指出哪些函数仍然使用旧式的类型码,这往往是个明智之举。
5.逐一修改源类用户,让它们使用新接口。
6.每修改一个用户,编译并测试。
à你也可能需要一次性修改多个彼此相关的函数,才能保持这些函数之间的一致性,才能顺利地编译、测试
7.删除使用类型码的旧接口,并删除保存旧类型码的静态变量。
8.编译,测试。
范例:
每个人都拥有四种血型中的一种。我们以Person来表示“人”,以其中的类型码表示“血型”:
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; pulic Person(int bloodGroup){ _bloodGroup = bloodGroup; } public void setBloodGroup(int arg){ _bloodGroup = arg; } public int getBloodGroup(){ return _bloodGroup; } }
首先,我们建立一个新的BloodGroup类,用以表示“血型”,并在这个类实例中保存原本的类型码数值:
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); public static final BloodGroup[] _values = {O, A, B, AB}; private final int _code; private BloodGroup(int code){ _code = code; } public int getCode(){ return _code; } public static BloodGroup code(int arg){ return _values[arg]; } }
然后,把Person中的类型码改为使用BloodGroup类:
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; pulic Person(int bloodGroup){ _bloodGroup = BloodGroup.code(bloodGroup); } public void setBloodGroup(int arg){ _bloodGroup = BloodGroup.code(arg); } public int getBloodGroup(){ return _bloodGroup.getCode(); } }
现在因为BloodGroup类而拥有了运行期检验能力。为了真正从这些改变中获利,我们还必须修改Person的用户,让它们以BloodGroup对象表示类型码,而不再使用整数。
首先,使用Rename Method修改类型码访问函数的名称,说明当前情况:
class Person... public int getBloodGroupCode(){ return _bloodGroup.getCode(); }
然后为Person加入一个新的取值函数,其中使用BloodGroup;
public BloodGroup getBloodGroup(){ return _bloodGroup; }
另外,还要建立新的构造函数和设值函数,让它们也使用BloodGroup;
public Person (BloodGroup bloodGroup){ _bloodGroup = bloodGroup; } public void setBloodGroup(BloodGroup arg){ _bloodGroup = arg; }
现在,继续处理person用户。此时应该注意,每次只处理一个用户,这样才可以保持小步前进。每个用户需要的修改方式可能不同,这使得修改过程更加棘手。对Person内的静态变量的所有引用点也需要修改。因此,下列代码:
Person thePerson = new Person(Person.A);
就变成了
Person thePerson = new Person(BloodGroup.A);
原来调用取值函数的代码必须改为调用BloodGroup的取值函数。因此下列代码
thePerson.getBloodGroupCode();
变成了
thePerson.getBloodGroup.getCode();
设值函数也一样。因此下列代码
thePerson.setBloodGroup(Person.AB);
变成了
thePerson.setBloodGroup(BloodGroup.AB);
修改完毕Person的所有用户之后,就可以删掉原本使用整数类型的那些旧的取值函数、构造函数、静态变量和设值函数了。
还可以将BloodGroup中使用整数类型的函数声明为private,因为再也没有人会使用它们了:
class BloodGroup... private int getCode(){ return _code; } private static BloodGroup code(int arg){ return _values[arg]; }
相关文章推荐
- 8.13 replace type code with class(以类取代类型码)
- 重构手法31:Replace Type Code with Subclass (以子类取代类型码)
- 重构手法31:Replace Type Code with Subclass (以子类取代类型码)
- ReplaceTypeCodeWithClass(以类取代类型码)
- 重构手法30:Replace Type Code with Class (以类取代类型码)
- 重构之2.Replace Type Code with Class(以类取代类型码)
- 重构手法30:Replace Type Code with Class (以类取代类型码)
- 重构之4.Replace Type Code with State/Strategy(以State/Strategy取代类型码)
- 8.14 replace type code with subclasses (以子类取代类型码)
- 8.15 replace type code with state/strategy (以state/strategy取代类型码)
- replace type code with class(重构)用类替换类型码
- 重构手法32:Replace Type Code with State/Strategy (以状态/策略取代类型码)
- Replace Type Code with Subclasses (以子类取代类型码)
- 重构之3.Replace Type Code with Subclasses(以子类取代类型码)
- Refactor: replace type code with class (c++)
- Replace Type Code with Class(以类取代型别码)
- Eclipse重构——Replace TypeCode with Class
- Replace Type Code With Class和Replace Type Code With Subclass和Replace Type Code With State/Strategy
- Eclipse重构——Replace TypeCode with Class
- 重构-代替类型代码几中方式的比较(Replace type code with ...)