Replace Conditional with Polymorphism (以多态取代条件表达式)

Summary 你手上有个条件表达式,它根据对象类型的不同而选择不同的行为。将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。

                                   140636_RUJd_134516.png

Motivation: 在面向对象术语中,听上去最高贵的词非“多态”莫属。多态最根本的好处就是:如果你需要根据对象的不同类型而采取不同的行为,多态使你不必编写明显的条件表达式。

正因为有了多态,所以你会发现:“类型码的switch语句”以及“基于类型名称的if-then-else语句”在面向对象程序中很少出现。

多态能够给你带来很多好处。如果同一组条件表达式在程序许多地点出现,那么使用多态的收益是最大的。使用条件表达式时,如果你想添加一种新类型,就必须查找并更新所有条件表达式。但如果改用多态,只需建立一个新的子类,并在其中提供适当的函数就行了。类的用户不需要了解这个子类,这就大大降低了系统各部分之间的依赖,使系统升级更加容易。

Mechanics:

使用Replace Conditional with Polymorphism之前,首先必须由一个继承结构。你可能已经通过先前的重构得到了这一结构。如果还没有,现在就需要建立它。

要建立继承结构,有两种选择:Replace Type Code with SubclassesReplace Type Code with State/Strategy。前一种做法比较简单,因此应该尽可能使用它。但如果你需要在对象创建好了之后修改类型码,就不能使用继承手法,只能使用State/Strategy模式。此外,如果由于其他原因,要重构的类已经有了子类,那么也得使用State/Strategy。记住,如果若干switch语句针对的是同一个类型码,你只需针对这个类型码建立一个继承结构就行了。

现在,可以向条件表达式开战了。你的目标可能是switch语句,也可能是if语句。

1.如果要处理的条件表达式是一个更大函数中的一部分,首先对条件表达式进行分析,然后使用Extract Method将它提炼到一个独立函数去。

2.如果有必要,使用Move Method将条件表达式放置到继承结构的顶端。

3.任选一个子类,在其中建立一个函数,使之覆写超类中容纳条件表达式的那个函数。将与该子类相关的条件表达式分支复制到新建函数中,并对它进行适当调整。

为了顺利进行这一步骤,你可能需要将超类中的某些private字段声明为protected

4.编译,测试。

5.在超类中删掉条件表达式内被复制了的分支。

6.编译,测试。

7.针对条件表达式的每个分支,重复上述过程,直到所有分支都被移到子类内的函数为止。

8.将超类之中容纳条件表达式的函数声明为抽象函数。

   范例:

请允许我继续使用“员工与薪资”这个简单而又乏味的例子。我们的类是从Replace Type Code with State/Strategy那个例子中拿来的,因此示意图如下所示:

141051_mIgY_134516.png

class Employee...
    int payAmount(){
        switch(getType()){
            case EmployeeType.ENGINEER:
                return _monthlySalary;
            case EmployeeType.SALESMAN:
                return _monthlySalary + _commission;
            case EmployeeType.MANAGER:
                return _monthlySalary + _bonus;
            default:
                throw new RuntimeException("Incorrect Employee");
        }
    }
    
    int getType(){
        return _type.getTypeCode();
    }
    private EmployeeType _type;
    
    abstract class EmployeeType...
        abstract int getTypeCode();
        
    class Engineer extends EmployeeType...
        int getTypeCode(){
            return Employee.ENGINEER;
        }
        
    ... and other subclasses

switch语句已经被很好地提炼出来,因此我们不必费劲再做一遍。不过我们需要将它移到EmployeeType类,因为EmployeeType才是要被继承的类。

class EmployeeType...
    int payAmount(Employee emp){
        switch (getTypeCode()){
            case ENGINEER:
                return emp.getMonthlySalary();
            case SALESMAN:
                return emp.getMonthlySalary() + emp.getCommission();
            case MANAGER:
                return emp.getMonthlySalary() + emp.getBonus();
            defaule:
                throw new RuntimeException("Incorrect Employee");
        }
    }

由于我们需要Employee的数据,所以需要将Employee对象作为参数传递给payAmount()。这些数据中的一部分也许可以移到EmployeeType来,但那时另一项重构需要关心的问题了。

调整代码,使之通过编译,然后我们修改Employee中的payAmount()函数,令它委托EmployeeType:

class Employee...
    int payAmount(){
        return _type.payAmount(this);
    }

现在,我们可以处理switch语句了。这个过程有点象淘气小男孩折磨一只昆虫--每次掰掉它一条腿。首先我们把switch语句中的Engineer这一分支复制到Engineer类:

class Engineer...
    int payAmount(Employee emp){
        return emp.getMonthlySalary();
    }

这个新函数覆写了超类中的switch语句内专门处理Engineer的分支。我们可以故意在case子句中放一个陷阱,检查Engineer子类是否正常工作:

class EmployeeType...
    int payAmount(Employee emp){
        switch(getTypeCode()){
            case EmployeeType.ENGINEER:
                throw new RuntimeException("should be being overriden");
            case SALESMAN:
                return emp.getMonthlySalary() + emp.getCommission();
            case MANAGER:
                return emp.getMonthlySalary() + emp.getBonus();
            defaule:
                throw new RuntimeException("Incorrect Employee");
        }
    }

接下来我们重复上述过程,直到所有分支都被去除为止:

class Salesman...
    int payAmount(Employee emp){
        return emp.getMonthlySalary() + emp.getCommission();
    }
    
 class Manager...
     int payAmount(Employee emp){
         return emp.getMonthlySalary() + emp.getBonus();
     }

然后将超类的payAmount()函数声明为抽象函数:

class EmployeeType...
    abstract int payAmount(Employee emp);


转载于:https://my.oschina.net/u/134516/blog/213786

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值