美文网首页
重构-Type Code / 条件语句 / vf 多态: UML

重构-Type Code / 条件语句 / vf 多态: UML

作者: my_passion | 来源:发表于2022-09-18 23:36 被阅读0次

脑图

类型码

(1) `不会出现在 条件语句(switch/if)中` 而影响类的行为 
    
    重构 1: Replace Type Code with Class

(2) else
    
    应该
        
        1) 先`去掉 类型码` 

            [1] if  
                1] `Type Code 宿主类 没有子类`, 或
                2] `类型码 在对象创建后 不会改变` 
            
                重构 2: Relpace Type Code with Subclass
                    |   |
                    |   |/
                    |   重构 4: `对 创建对象的 switch 用 Relpace Ctor with Factory Method`
                    |   |
                    |/  |/
                    重构 5: `Replace Conditional with polymorphism` => 跳过后续 2)
                
            [2] if
                1] `Type Code 宿主类 有子类`, 或
                2] `类型码 在对象创建后 会改变` 
            
                重构 3: Relpace Type Code with State/Strategy
                |   |   | 类型码 field 换为 新类的指针 EmployeeType*`
                |   |   |
                |   |   |/
                |   |   重构 4
                |   |   |
                |   |/  |/
                |   重构 5
                |/
                重构 6: 将 `类型码 info(类型码枚举值/编译期常量 ENGINEER / MANAGER)` 
                        和 `子类(Engineer / Manager) info` 均移到 新类 EmployeeType

        2) 再`用 vf/多态 取代 条件语句`
        
            重构 5: Replace Conditional with Ploymorphism 
        
                好处: `把 "对不同行为的了解" 从类的用户转移到类自身. 
                        若需要新增新的行为变化, 只需要增加1个子类即可.`
                    
                若无多态机制, 就必须找到所有条件语句, 修改
                
        3)  将`只与特定子类相关`的 `字段和函数 下推`到相关子类
        
            重构 7: Push Down Field & Method

1 Replace Type Code with Class

动机 
    类含 (数值)`Type Code/枚举值` -> `不能` 实现 `编译器/运行期 类型检查` -> `bug 之源`
    
前提
    `Type Code` 是 纯粹数据, `不会出现在条件语句(switch/if)中而影响类的行为`

做法
    Type Code -> Type Code 类, 可实现 运行期 类型检查
    
        类型码原宿主类              
            [1] 类型码字段(field) 改用 新类(类型码类) objPtr, 
            [2] Ctor / 设值函数 / 取值函数 的 para或returnValue 改用 新类(Ptr)
            
        新类(类型码类)
            [1] Note1: `创建静态对象用工厂函数, 保证了只有合法的 静态对象 才会被创建`
            [2] `一组静态变量` 保存允许被创建的 object    
     ———————————————————————————————————————
    |   Person                              |
     ———————————————————————————————————————
    | + O: static int                     ——| —— ——  = 0
    | + A: static int                     ——| —— ——  = 1
    |                                       |
    | - bloodType: int                      |
     ———————————————————————————————————————
    | + Person(int bloodType_)              |
    | + setBloodType(int bloodType_):void   |
    | + getBloodType(): int                 |
     ————————————————————————————————————————
                             | 
                             | 重构 1
                             |/
     ————————————————————————————————————————————        ————————————————————————————————————————————————————               
    |   Person                                  |     \ |   BloodType                                       |   
     ———————————————————————————————————————————— ————— —————————————————————————————————————————————————————    
    | - bloodType: BloodType*                   |     / | + O: static BloodType*                            |                                                       
     ————————————————————————————————————————————       | + A: static BloodType*                            |                       
    | + Person(BloodType* bloodType_)           |       |                                                   |             
    | + setBloodType(BloodType* bloodType_)     |       | - typeCode: int                                   |                 
    | + getBloodType(): BloodType*              |       | - bloodTypeVec: static vector<BloodType*>         |    
     ————————————————————————————————————————————        ————————————————————————————————————————————————————               
                                                        | + BloodType(int typeCode_)                        |   
                                                        |                                                   |
                                                        | - getBloodType(int typeCode_): static BloodType*  |   
                                                        | - getTypeCode() : int                         |   
                                                        ————————————————————————————————————————————————————    
                                                                                                                
                                                            `工厂函数, 保证了只有合法 静态对象 才会被创建`      
                                                                BloodType* BloodType::O = new BloodType(0);
                                                            
                                                            static vector<BloodType> bloodTypeVec = {O, A}
                                                            
    Client  // #include "Person.h"                                                  
        Person* person = new Person(BloodType::A);
        BloodType* bloodType = person->getBloodType();
        person->setBloodType(BloodType::O);

2 Replace Type Code with SubClass

动机
    类 中 type code 为 数值, 影响 类 的 行为(据 不同 type code 执行 不同动作)
做法
    以 多态 处理 变化
    
        Replace Type Code with SubClass
            以 type code 宿主类 为 Base 类, 对 每种 type code 建相应 Derived 类
            
                    +

        Replace Conditional with Polymorphism
     ———————————————————————————————————————
    |   Employee                            |
    ————————————————————————————————————————
    | - typeCode: int                       |
    | - monthlySalary                       |
    | - bonus                               |
    |                                       |
    | + ENGINEER: static const int = 0      |
    | + MANAGER: static const int = 1       |
     ———————————————————————————————————————
    | + Employee(int typeCode_)             |
    |                                       |
    | + payAmount(): int                 - -| - -  switch (typeCode)
     ———————————————————————————————————————
        
            Client 
                pEmployee = new Employee(Employee::ENGINEER);
                pEmployee->payAmount();

Step1 类型码替换为子类 + Ctor 替换为工厂方法(Refactor2+Refactor4)

基类

(1) 去掉 类型码 field

=> 所有 non-vf: (新增)静态工厂方法 + 其他 non-vf(即 条件函数) 如何知道各子类对象的类型码 ? -> 类型码 形参

=> 条件函数类型码实参 -> 用 新增 获取子类类型码 的 vf 的调用

(2) 去掉 Ctor + 新增 静态工厂方法

     ————————————————————————————————————————————
    |   Employee                                |
    —————————————————————————————————————————————
    | - monthlySalary                           |
    | - bonus                                   |   
    |                                           |
    | + ENGINEER: static int = 0                |
    | + MANAGER: static int = 1             |
     ————————————————————————————————————————————           // switch 只在 创建 对象 时有               
    | + create(int typeCode_): static Employee* | - - switch (typeCode_) { case ENGINEER: return new Engineer(); // ... }   
    |                                           |
    | + payAmount(int typeCode_): int       - - | - - switch (typeCode_) { case ENGINEER: return monthlySalary; // ... } 
    |                                           |
    | + getTypeCode(): virtual int = 0          |       
     ————————————————————————————————————————————
                        /\                          
                       /__\                          
                        | |
                        | |_ _ _ _ _ _ _
                        |               |
     ————————————————————————        ————————————————————————
    |   Engineer            |       |   Manager             |
     ————————————————————————        ————————————————————————
    |                       |       |                       |
     ————————————————————————        ————————————————————————
    | + getTypeCode(): int  |       | + getTypeCode(): int  |
     —————|———————————————————       —————|———————————————————  
          |                               |
         return Employee::ENGINEER;      return Employee::MANAGER;
                     
        
    Client:
        Employee* pEmployee = Employee::create(Employee::ENGINEER);
        pEmployee->payAmount(pEmployee->getTypeCode() );

Step2 条件函数换为 多态

(1) 条件函数non-vf 换成 vf =>

[1] vf 不再需要 类型码形参(虚调用机制 子类用自己的 type_info 告诉自己的 类型码) => 条件函数不再需要 类型码实参 => 类层次 不再需要 获取子类类型码的 vf

[2] vf 内需要 获取 基类的成员 => 基类 新增 get 函数

     ————————————————————————————————————————————
    |   Employee                                |
    —————————————————————————————————————————————
    | - monthlySalary                           |
    | - bonus                                   |   
    |                                           |
    | + ENGINEER: constexpr int = 0             |
    | + MANAGER: constexpr int = 1              |
     ————————————————————————————————————————————                       
    | + create(int typeCode_): static Employee* |
    |                                           |
    | + payAmount(int typeCode_): virtual int=0 |   
    | + getMonthlySalary(): int                 |
    | + getBonus(): int                         |   
     ————————————————————————————————————————————
                        /\                          
                       /__\                          
                        | |
                        | |_ _ _ _ _ _ _ _ _ _ _ _ _ 
                        |                           |
     ———————————————————————————————     ———————————————————————————————        
    |   Engineer                    |   |   Manager                     |       
     ———————————————————————————————     ———————————————————————————————        
    |                               |   |                               |       
     ———————————————————————————————     ———————————————————————————————   
    |   + payAmount(): virtual int  |   |  + payAmount(): virtual int   |       
     ———————|————————————————————————    ——————-|—————————————————————————      
            |                                   |
        return getMonthlySalary();          return getMonthlySalary() + getBonus();
        
        
    Client:
        Employee* pEmployee = Employee::create(Employee::ENGINEER);
        pEmployee->payAmount();

Step3 Push Down Field & Method: 将只与特定子类相关的字段和函数下推到相关子类

只变动4个文件: Employee.h/cpp Manager.h/cpp

    ————————————————————————————————————————————
    |   Employee                                |
    —————————————————————————————————————————————
    | - monthlySalary                           |   
    |                                           |
    | + ENGINEER: constexpr int = 0             |
    | + MANAGER: constexpr int = 1              |
     ————————————————————————————————————————————                       
    | + create(int typeCode_): static Employee* |
    |                                           |
    | + payAmount(int typeCode_): virtual int=0 |   
    |                                           |
    | + getMonthlySalary(): int                 |
     ————————————————————————————————————————————
                        /\                          
                       /__\                          
                        | |
                        | |_ _ _ _ _ _ _ _ _ _ _ _ _ 
                        |                           |
     ———————————————————————————————     ———————————————————————————————        
    |   Engineer                    |   |   Manager                     |       
     ———————————————————————————————     ———————————————————————————————        
    |                               |   | - bonus                       |                           |       
     ———————————————————————————————     ———————————————————————————————  
    |                               |   | + getBonus(): int             |   
    | + payAmount(): virtual int    |   | + payAmount(): virtual int    |       
     ———————|————————————————————————    ——————-|—————————————————————————      
            |                                   |
        return getMonthlySalary();          return getMonthlySalary() + getBonus();

3 Replace Type Code with Sate/Strategy

动机
    `type code 可变` => 不能用 继承 消除它
做法
    type code 替换为 状态对象

类型码可变 =>

(1) 需要1个 设置 类型码的函数 setType(typeCode_): 只修改类型码

(2) 条件函数中无需类型码参数, switch 中放的固定类型码换为 获取类型码的函数 getType() 的调用


    ———————————————————————————————————————
    |   Employee                            |
    ————————————————————————————————————————
    | - type: int                           |
    | - monthlySalary                       |
    | - bonus                               |
    |                                       |
    | + ENGINEER: static constexpr int = 0  |   
    | + MANAGER: static constexpr int = 1   |   
     ———————————————————————————————————————
    | + Employee(int typeCode_)             |
    |                                       |
    | + payAmount(): int                 - -| - - switch (getType() ) // typeCode can be change
    | + getType(): int                      |           
    |                                       | 
    | + setType(int typeCode_): void     - -| - - type = typeCode_; 
     ———————————————————————————————————————
        
            Client 
                pEmployee = new Employee(Employee::ENGINEER);
                pEmployee->payAmount();
                pEmployee->setType(Employee::MANAGER);
                pEmployee->payAmount();

Step1: 类型码 field 换为 新类的指针 EmployeeType* (重构 3)

(1) 新建1个类型码类

[1] 其指针放 原类型码所属类 -> Composition

[2] 只放1个 获取子类类型码的 vf

[3] 再新建其 子类

(2) 设置 类型码的函数 setType(typeCode_):

(重)建 子类对象: new 子类对象 + 赋值给 类型码类指针

[1] 被 Ctor 调用, 初建

[2] 被 Client 调用, 重建

(3) 原类 获取类型码的函数 getType() (non-vf) 转调 类型码类层次的 获取类型码的函数 getTypeCode() (vf)

    ———————————————————————————————————————
    |   Employee                            |                ————————————————————————————————————
    ————————————————————————————————————————                |   EmployeeType                    |
    | - type: EmployeeType*                 |             \  ————————————————————————————————————
    | - monthlySalary                       |  ———————————— |                                   |
    | - bonus                               |             /  ————————————————————————————————————
    |                                       |               | + getTypeCode():virtual int = 0   |
    | + ENGINEER: static constexpr int = 0  |               ———————————————————————————————————— 
    | + MANAGER: static constexpr int = 1   | \                         /\  
     ———————————————————————————————————————   \                       /__\
    | + Employee(int typeCode_)             |   \                       |    add 子类: 每个子类对应1种 type 
    | + setType(int typeCode_): void        |    \                      |   
    |                       |               |     \ include     ———————————————————————————————
- - | + payAmount(): int    |               |      \- - - - - ->|   Engineer                    |
|   | + getType(): int      |               |       |            ———————————————————————————————
|    ———————|———————————————|————————————————       |           |                               |
|           |               |                       |           ———————————————————————————————
|           |               switch (typeCode_)      |           | + getTypeCode(): virtual int  |
|           |                   case ENGINEER:      |           ——————————|————————————————————— 
|           |                       type = new Engineer(); ;              |     
|       type->getTypeCode();        break                           return Employee::ENGINEER
|                                                           
|- - switch (getType() )        

Step2: 将 类型码 info(类型码枚举值/编译期常量 ENGINEER / MANAGER) 移到 类型码类 EmployeeType (重构 6)

(1) 类型码常量 移动 => 条件函数中 case 类型码常量 换为 case 类型码类::类型码常量

(2) 原类 setType()条件 (重)建(new)子类对象/Ctor 换为 转调用 类型码类的 工厂函数

    ———————————————————————————————————————                  ———————————————————————————————————————————————
    |   Employee                            |               |   EmployeeType                                |
    ————————————————————————————————————————                 ———————————————————————————————————————————————
    | - type: EmployeeType*                 |             \ | + ENGINEER: static constexpr int = 0          |               
    | - monthlySalary                       |  ———————————— | + MANAGER: static constexpr int = 1           |
    | - bonus                               |             /  ———————————————————————————————————————————————             
     ———————————————————————————————————————    - - - - - ->| + newType(int typeCode): static EmployeeType* | - - switch (typeCode_) 
    | + Employee(int typeCode_)             |   |           |                                               |       case ENGINEER:
    | + setType(int typeCode_): void        |   |       - ->| + getTypeCode(): virtual int = 0              |           return new Engineer();
    |                       |               |   |       |   —————————————————————————————————————————————————  
- - | + payAmount(): int    |               |   |       |                    /\ 
|   | + getType(): int      |               |   |       |                   /__\
|    ———————|———————————————|————————————————   |       |                    |   
|           |               |                   |       |                    |  
|           | type = EmployeeType::newType(typeCode_);  |       ———————————————————————————————
|           |                                           |       |   Engineer                    |
|           |                                           |        ———————————————————————————————
|       type->getTypeCode();  - - - - - - - - - - - - - -       |                               |
|                                                               ———————————————————————————————
|- - switch (getType() )                                        | + getTypeCode(): virtual int  |
        case EmployeeType::ENGINEER:                            ——————————|————————————————————— 
            return monthlySalary;                                         |     
                                                                    return Employee::ENGINEER

Step3: 原类的 条件函数(non-vf) 转调 类型码类中新增的相应 多态 vf

(1) 后者想获取前者中的 成员数据 => 通过前者的 成员函数 => 需要拿到 原类对象的指针 => 前者转调后者时, 以自身对象的指针 this 作实参 =>

[1] 原类要新增 getFunc()

[2] 原类/新类 getType()/getTypeCode() 均不再需要

(2) 新类不 include(而是 前向声明)原宿主类, 具体子类(Engineer.h) include(依赖)新类(EmployeeType.h) 和原宿主类(Employee.h), 但

    ———————————————————————————————————————                  ———————————————————————————————————————————————————
    |   Employee                            |               |   EmployeeType                                    |
    ————————————————————————————————————————                 ———————————————————————————————————————————————————
    | - type: EmployeeType*                 |             \ | + ENGINEER: static constexpr int = 0              |               
    | - monthlySalary                       |  ———————————— | + MANAGER: static constexpr int = 1               |
    | - bonus                               |             /  ———————————————————————————————————————————————————             
     ———————————————————————————————————————    - - - - - ->| + newType(int typeCode): static EmployeeType*     | - - switch (typeCode_) 
    | + Employee(int typeCode_)             |   |           |                                                   |       case ENGINEER:
    | + setType(int typeCode_): void        |   |       |-->| + payAmount(Employee* pEmployee):virtual int = 0  |           return new Engineer();
    |                       |               |   |       |   ———————————————————————————————————————————————————— 
    | + payAmount(): int    |               |   |       |                    /\ 
    |       |               |               |   |       |                   /__\
- ->| + getMonthlySalary(): | int           |   |       |                    |
|    ———————|———————————————|————————————————   |       |                    |   
|  /|\      |               |                   |       |                    |  
|   |       | type = EmployeeType::newType(typeCode_);  |        ———————————————————————————————————————————————
|   |       |                                           |       |   Engineer                                    |
|   |       |                                           |        ———————————————————————————————————————————————
|   |   return type->payAmount(this); - - - - - - - - - -       |                                               |
|   |                                                            ———————————————————————————————————————————————
|   |- - - - - - - - - - - - - - - - - - - - - - - - - - - - -  | + payAmount(Employee* pEmployee): virtual int |
|                           #include(依赖)                        ——————————|————————————————————— ————————————————
|                                                                         |     
|                                                                         |
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - return pEmployee->getMonthlySalary();

Step4: Push Down Field & Method (重构 7)

方法同 2.3

相关文章