文档章节

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

忆瑶
 忆瑶
发布于 2014/03/28 14:31
字数 1330
阅读 599
收藏 1

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

                                   

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那个例子中拿来的,因此示意图如下所示:

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);


© 著作权归作者所有

忆瑶
粉丝 19
博文 133
码字总数 99861
作品 0
杭州
高级程序员
私信 提问
重构-改善既有代码的设计-简化条件表达式

简化条件表达式相关重构手法 1.decompose conditional(分解条件表达式)(你有一个复杂的条件if then else语句,从if then else 中提炼出独立的函数) 如 2.consolidate conditional expres...

梦想游戏人
2016/05/18
46
0
Replace Type Code with Subclasses (以子类取代类型码)

Summary: 你有一个不可变的类型码,它会影响类的行为。以子类取代这个类型码。 动机: 如果你面对的类型码不会影响宿主类的行为,可以使用Replace Type Code with Class来处理它们。但如果类...

忆瑶
2014/03/25
169
0
开发中滥用面向对象,你是否违背了编程原则

Switch 声明 Switch 声明(Switch Statements) 你有一个复杂的 语句或 序列语句。 问题原因 面向对象程序的一个最明显特征就是:少用 和 语句。从本质上说, 语句的问题在于重复( 序列也同样...

李红欧巴
05/09
820
1
Replace Parameter with Explicit Methods (以明确函数取代参数

Summary: 你有一个函数,其中完全取决于参数值而采取不同行为。针对该参数的每一个可能值,建立一个独立函数。 动机: Replace Parameter with Explicit Methods 恰恰相反于Parameterize Me...

忆瑶
2014/04/08
121
0
Replace Type Code with State/Strategy

Summary: 你有一个类型码,它会影响类的行为,但你无法通过继承手法消除它。以状态对象取代类型码。 动机: 本项重构和Replace Type Code with Subclasses很相似,但如果“类型码的值在对象生...

忆瑶
2014/03/25
92
0

没有更多内容

加载失败,请刷新页面

加载更多

刚哥谈架构 (二) 我眼中的架构师

之前在公司,有小伙伴在向别人介绍我的时候,经常会有人这么说:“刚哥是我们的architcture”,如果来人是老外,心中一定是一惊,心中暗叹,“这位匪首看上去貌不惊人,难道已经做到了架构和...

naughty
27分钟前
2
0
OSChina 周日乱弹 —— 别问,问就是没空

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @tom_tdhzz :#今日歌曲推荐# 分享容祖儿/彭羚的单曲《心淡》: 《心淡》- 容祖儿/彭羚 手机党少年们想听歌,请使劲儿戳(这里) @wqp0010 :周...

小小编辑
56分钟前
44
2
golang微服务框架go-micro 入门笔记2.1 micro工具之micro api

micro api micro 功能非常强大,本文将详细阐述micro api 命令行的功能 重要的事情说3次 本文全部代码https://idea.techidea8.com/open/idea.shtml?id=6 本文全部代码https://idea.techidea8....

非正式解决方案
今天
5
0
Spring Context 你真的懂了吗

今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识。 1. context 是什么 我们经常在编程中见到 context 这个单词,当...

Java知其所以然
昨天
5
0
Spring Boot + Mybatis-Plus 集成与使用(二)

前言: 本章节介绍MyBatis-Puls的CRUD使用。在开始之前,先简单讲解下上章节关于Spring Boot是如何自动配置MyBatis-Plus。 一、自动配置 当Spring Boot应用从主方法main()启动后,首先加载S...

伴学编程
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部