重构数据-Replace Type Code with Class以类取代类型码十二
创始人
2025-05-31 13:06:24

重构数据-Replace Type Code with Class以类取代类型码十二

1.以类取代类型码

1.1.使用场景

1.使用场景

类之中有一个数值类型码,但它并不影响类的行为。以一个新的类替换该数值类型码

在以C为基础的编程语言中,类型码或枚举值很常见。如果带着一个有意义的符号名,类型码的可读性还是不错的。问题在于,符号名终究只是个别名,编译器看见的、进行类型检验的,还是背后那个数值。任何接受类型码作为参数的函数,所期望的实际上是一个数值,无法强制使用符号名。这会大大降低代码的可读性,从而成为bug之源。

如果把那样的数值换成一个类,编译器就可以对这个类进行类型检验。只要为这个类提供工厂函数,你就可以始终保证只有合法的实例才会被创建出来,而且它们都会被传递给正确的宿主对象。

2.注意事项

但是,在使用Replace Type Code with Class (218)之前,你应该先考虑类型码的其他替换方式。只有当类型码是纯粹数据时(也就是类型码不会在switch语句中引起行为变化时),你才能以类来取代它。

Java只能以整数作为switch语句的判断依据,不能使用任意类,因此那种情况下不能够以类替换类型码。更重要的是:任何switch语句都应该运用Replace Conditional with Polymorphism (255)去掉。为了进行那样的重构,你首先必须运用Replace Type Code with Subclasses (223)或Replace Type Code with State/Strategy (227),把类型码处理掉。

即使一个类型码不会因其数值的不同而引起行为上的差异,宿主类中的某些行为还是有可能更适合置放于类型码类中,因此你还应该留意是否有必要使用Move Method (142)将一两个函数搬过去。

1.2.如何做

  • 为类型码建立一个类。
  • 这个类需要一个用以记录类型码的字段,其类型应该和类型码相同,并应该有对应的取值函数。此外还应该用一组静态变量保存允许被创建的实例,并以一个静态函数根据原本的类型码返回合适的实例。
  • 修改源类实现,让它使用上述新建的类。
  • 维持原先以类型码为基础的函数接口,但改变静态字段,以新建的类产生代码。然后,修改类型码相关函数,让它们也从新建的类中获取类型码。
  • 编译,测试。
  • 此时,新建的类可以对类型码进行运行期检查。
  • 对于源类中每一个使用类型码的函数,相应建立一个函数,让新函数使用新建的类。
  • 你需要建立“以新类实例为自变量”的函数,用以替换原先“直接以类型码为参数”的函数。你还需要建立一个“返回新类实例”的函数,用以替换原先“直接返回类型码”的函数。建立新函数前,你可以使用Rename Method (273)修改原函数名称,明确指出哪些函数仍然使用旧式的类型码,这往往是个明智之举。
  • 逐一修改源类用户,让它们使用新接口。
  • 每修改一个用户,编译并测试。
  • 你也可能需要一次性修改多个彼此相关的函数,才能保持这些函数之间的一致性,才能顺利地编译、测试。
  • 删除使用类型码的旧接口,并删除保存旧类型码的静态变量。
  • 编译,测试。

1.3. 示例

每个人都拥有四种血型中的一种。我们以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 (int bloodGroup) {_bloodGroup = bloodGroup;}public void setBloodGroup(int arg) {_bloodGroup = arg;}public int getBloodGroup() {return _bloodGroup;}}

首先,我建立一个新的BloodGroup class,用以表示「血型」,并在这个实体中保存原本的type code 数值:

 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 final int _code;private BloodGroup (int code ) {_code = code;}public int getCode() {return _code;}public static BloodGroup code(int arg) {return _values[arg];}}

然后,我把Person 中的type code 改为使用BloodGroup 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 (int bloodGroup) {_bloodGroup = BloodGroup.code(bloodGroup);}public int getBloodGroup() {return _bloodGroup.getCode();}public void setBloodGroup(int arg) {_bloodGroup = BloodGroup.code (arg);}}

现在,我因为BloodGroup class 而拥有了运行期检验能力。为了真正从这些改变中获利,我还必须修改Person 的用户,让它们以BloodGroup 对象表示type code,而不再使用整数。
首先,我使用Rename Method 修改type code 访问函数的名称,说明当前情况:

 class Person...public int getBloodGroupCode() {return _bloodGroup.getCode();}

然后我为Person 加入一个新的取值函数(getter),其中使用BloodGroup

   public BloodGroup getBloodGroup() {return _bloodGroup;}

另外,我还要建立新的构造函数和设值函数(setter),让它们也使用BloodGroup :

 public Person (BloodGroup bloodGroup ) {_bloodGroup = bloodGroup;}public void setBloodGroup(BloodGroup arg) {_bloodGroup = arg;}

现在,我要继续处理Person 用户。此时应该注意,每次只处理一个用户,这样才可以保持小步前进。每个用户需要的修改方式可能不同,这使得修改过程更加棘手。 对Person 的static 变量的所有引用点也需要修改。因此,下列代码:

 Person thePerson = new Person(Person.A)

就变成了:

Person thePerson = new Person(BloodGroup.A);

调用取值函数(getter)必须改为调用新取值函数。因此,下列代码:

 thePerson.getBloodGroupCode()

变成了 :

 thePerson.getBloodGroup().getCode()

修改完毕Person 的所有用户之后,我就可以删掉原本使用整数型别的那些旧的取值函数、构造函数、静态变量和设值函数了:

 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();public Person (int bloodGroup) {_bloodGroup = BloodGroup.code(bloodGroup);}         public int getBloodGroup() {return _bloodGroup.getCode();}public void setBloodGroup(int arg) {_bloodGroup = BloodGroup.code (arg);}

我还可以将BloodGroup 中使用整数型别的函数声明为private (因为再没有人会使用它们了)

 class BloodGroup...private int getCode() {return _code;}private static BloodGroup code(int arg) {return _values[arg];}

相关内容

热门资讯

玩家攻略“家家乐牌吧究竟有挂吗... 玩家攻略“家家乐牌吧究竟有挂吗”(详细开挂教程)亲,家家乐牌吧这个游戏其实有挂的,确实是有挂的,需要...
今日重大消息“中至麻将可以开挂... 今日重大消息“中至麻将可以开挂吗”[其实是有挂]您好:中至麻将这款游戏可以开挂,确实是有挂的,需要了...
今日重大通报“掌酷天天开心到底... 今日重大通报“掌酷天天开心到底是不是挂”[果然有透视挂]您好:掌酷天天开心这款游戏可以开挂,确实是有...
实测讲解“白金岛游戏开挂使用方... 您好,白金岛游戏这款游戏可以开挂的,确实是有挂的,需要了解加微【9752949】很多玩家在这款游戏中...
透明辅助[微扑克有没有稳赢的打... 透明辅助[微扑克有没有稳赢的打法]wepoke能赢嘛(2024新版教程)-哔哩哔哩1、微扑克有没有稳...