文档章节

Replace Type Code with State/Strategy

忆瑶
 忆瑶
发布于 2014/03/25 15:38
字数 1237
阅读 92
收藏 0

Summary 你有一个类型码,它会影响类的行为,但你无法通过继承手法消除它。以状态对象取代类型码。                                               

动机:

  本项重构和Replace Type Code with Subclasses很相似,但如果“类型码的值在对象生命期中发生变化”或“其他原因使得宿主类不能被继承”,你也可以使用本重构。本重构使用State模式或Strategy模式。

State模式和Strategy模式非常相似,因此无论你选择其中哪一种,重构过程都是相同的。“选择哪一个模式”并非问题关键所在,你只需要选择更适合特定情境的模式就行了。如果你打算在本项重构完成之后再以Replace Conditional with Polymorphism简化一个算法,那么选择Strategy模式比较合适;如果你打算搬移与状态相关的数据,而且你把新建对象视为一种变迁状态,就应该选择使用State模式。

做法:

1.使用Self Encapsulate Field将类型码自我封装起来。

2.新建一个类,根据类型码的用途为它命名。这就是一个状态对象。

3.为这个新类添加子类,每个子类对应一种类型码。

à比起逐一添加,一次性加入所有必要的子类可能更简单些。

4.在超类中建立一个抽象的查询函数,用以返回类型码。在每个子类中覆写该函数,返回确切的类型码。

5.编译。

6.在源类中建立一个字段,用以保存新建的状态对象。

7.调整源类中负责查询类型码的函数,将查询动作转发给状态对象。

8.调整源类中为类型码设值的函数,将一个恰当的状态对象子类赋值给“保存状态对象”的那个字段。

9.编译,测试。

   范例:

    和上一项重构一样,我们仍然使用“雇员/薪资”例子。同样的,我们以Employee表示“雇员”:

class Employee{  
    private int _type;    
    static final int ENGINEER = 0;    
    static final int SALESMAN = 1;    
    static final int MANAGER =2;    
   
    Employee (int type){    
        _type = type;
    }    
}

下面的代码展示使用这些类型码的条件表达式:

int payAmount(){
    switch (_type){
        case ENGINEER:
            return _monthlySalary;
        case SALESMAN:
            return _monthlySalary + _commission;
        case MANAGER:
            return _monthlySalary + _bonus;
        default:
            return new RuntimeException("Incorrect Employee");
    }
}

假设这是一家激情四溢、积极进取的公司,他们可以将表现出色的工程师擢升为经理。因此,对象的类型码是可变的,所以我们不能使用继承的方式来处理类型码。和以前一样,我们的第一步还是使用Self Encapsulate Field将表示类型码的字段自我封装起来:

Employee(int type){
    setType(type);
}

int getType(){
    return _type;
}
void setType(int arg){
    _type = type;
}

int payAmount(){
    switch (getType()){
        case ENGINEER:
            return _monthlySalary;
        case SALESMAN:
            return _monthlySalary + _commission;
        case MANAGER:
            return _monthlySalary + _bonus;
        default:
            return new RuntimeException("Incorrect Employee");
    }
}

现在,我们需要声明一个状态类。我们把它声明为一个抽象类,并提供一个抽象函数,用以返回类型码:

abstract class EmployeeType{
    abstract int getTypeCode();
}

现在可以开始创造子类了:

class Engineer extends EmployeeType{
    int getTypeCode(){
        return Employee.ENGINEER;
    }
}

class Manager extends EmployeeType{
    int getTypeCode(){
        return Employee.MANAGER;
    }
}

class Salesman extends EmployeeType{
    int getTypeCode(){
        return Employee.SALESMAN;
    }
}

现在进行一次编译。前面所做的修改实在太平淡了。现在我们开始修改类型码访问函数,实实在在地把这些子类和Employ类联系起来:

class Employee...
    private EmployeeType _type;
    
    int getType(){
        return _type.getTypeCode();
    }
    
    void setType(int arg){
        switch(arg){
            case ENGINEER:
                _type = new Engineer();
                break;
            case SALESMAN:
                _type = new Salesman();
                break;
            case MANAGER:
                _type = new Manager();
                break;
            default:
                throw new IllegalArgumentException("Incorrect Employee Code");
        }
    }

这意味着我们将在这里拥有一个switch语句。完成重构之后,这将是代码中唯一的switch语句,并且只在对象类型发生改变时才会执行。我们也可以运用Replace Constructor with Factory Method针对不同的case子句建立相应的工厂函数。还可以立刻再使用Replace Conditional with Polymorphism,从而将其他的case子句完全消除。

 最后,可以将所有关于类型码和子类的知识都移到新类,并依此结束Replace Type Code with State/Strategy。首先,我们把类型码的定义复制到EmployeeType去,在其中建立一个工厂函数以生成适当的EmployeeType对象,并调整Employee中为类型码赋值的函数:

class Employee...
    void setType(int arg){
        _type = EmployeeType.newType(arg);
    }

calss EmployeeType...
    static EmployeeType newType(int code){
        switch(code){
            case ENGINEER:
                return new Engineer();
            case SALESMAN:
                return new Salesman();
            case MANAGER:
                return new Manager();
            default:
                throw new IllegalArgumentException("Incorrect Employee Code");
        }
    }
    static final int ENGINEER = 0;
    static final int SALESMAN = 1;
    static final int MANAGER = 2;

然后,删掉Employee中的类型码定义,代之以一个纸箱EmployeeType对象的引用:

class Employee...
    int payAmount(){
    switch (getType()){
        case EmployeeType.ENGINEER:
            return _monthlySalary;
        case EmployeeType.SALESMAN:
            return _monthlySalary + _commission;
        case EmployeeType.MANAGER:
            return _monthlySalary + _bonus;
        default:
            return new RuntimeException("Incorrect Employee");
    }
}

现在,完事具备,可以运用Replace Conditional with Polymorphism来处理payAmount()函数了。

© 著作权归作者所有

忆瑶
粉丝 19
博文 133
码字总数 99861
作品 0
杭州
高级程序员
私信 提问
Replace Conditional with Polymorphism (以多态取代条件表达式)

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

忆瑶
2014/03/28
599
0
重构-改善既有代码的设计-重新组织数据

重新组织数据相关重构手法 1.Self Encapsulate Field(自封装字段)(你直接访问一个字段,但是字段之间的耦合变得笨拙,为这个字段建立setter getter 并只用函数来访问字段) 2.Replace Da...

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

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

忆瑶
2014/03/25
169
0
Replace Type Code with Class (以类取代类型码)

Summary: 类之中有一个数值类型码,但它并不影响类的行为。以一个新的类替换该数值类型码。 动机: 在以C为基础的变成语言中,类型码或枚举值很常见。如果带着一个有意义的符号名,类型码的可...

忆瑶
2014/03/24
305
0
使用MegaCli监控H700 Raid卡磁盘

使用过程中发现有多个Virtual Drive存在的问题,因此在上一个版本上做了改进。 zabbix_agentd userparams配置 zabbix_server调用 源码如下: H700 Raid卡磁盘监控扩展版(包括对多个Virtual...

Kevalin
2015/12/28
105
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Boot + Mybatis-Plus 集成与使用(二)

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

伴学编程
今天
7
0
用最通俗的方法讲spring [一] ──── AOP

@[TOC](用最通俗的方法讲spring [一] ──── AOP) 写这个系列的目的(可以跳过不看) 自己写这个系列的目的,是因为自己是个比较笨的人,我曾一度怀疑自己的智商不适合干编程这个行业.因为在我...

小贼贼子
今天
7
0
Flutter系列之在 macOS 上安装和配置 Flutter 开发环境

本文为Flutter开发环境在macOS下安装全过程: 一、系统配置要求 想要安装并运行 Flutter,你的开发环境需要最低满足以下要求: 操作系统:macOS(64位) 磁盘空间:700 MB(不包含 IDE 或其余...

過愙
今天
6
0
OSChina 周六乱弹 —— 早上儿子问我他是怎么来的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @凉小生 :#今日歌曲推荐# 少点戾气,愿你和这个世界温柔以待。中岛美嘉的单曲《僕が死のうと思ったのは (曾经我也想过一了百了)》 《僕が死の...

小小编辑
今天
2.5K
16
Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
42
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部