elementUIalready

第21章 类层次: UI 设计 & 不涉及 事件驱动技术

2022-06-25  本文已影响0人  my_passion

Note

(1)
    ————————————————————————————————————————————
    用 `抽象类`                支持 `接口继承` 
    
    用 `带 vf实现 的 基类` 支持 `实现继承` 
    ————————————————————————————————————————————
    
(2) 带限定符的调用 不会用到 虚调用机制

21.1 设计 类层次: 建立 "虚拟 UI 系统" - 分离 UI 与 应用程序

1 实现继承

(1) Base/Ival_box 类

    1) 带状态: low/high/changed 等属性
        
    2) 定义 成员 vf/non-vf
                                          
(2) Base/Ival_box 作类层次的 `接口类`  

    1) Derived 对象创建 函数 
        
        返回 智能指针 -> sp<Derived>.get() 取出 internal ptr 
            -> 隐转为 `Base/Ival_box 指针` 作 实参, 调 interact(Ival_box*)
        
    2) 与 `user 程序` 的 `交互接口`
                        
        void interact(Ival_box* pb);
        
    3)  类层次                         
                                BBwidget
                                    |
                                    |   public 继承
                                    |
                                Ival_box
                box 形式   /           \
                            /             \
                           /               \    
                         滑块                 旋钮 
                       /    \
                      /      \ 响应 prompt() 的方式
                     /        \
        出现在某处重要位置     闪烁  
                    
                                
(3) 4 个问题                   
    
    2) Ival_box data 是 实现细节, 却混入 Ival_box 接口
    
    2) UI 系统(BBwidget) 是 实现细节 ( BBwidget ), 却被拔高到 `高层策略` 

    3) UI 系统改动 会导致 `user 代码 重新编译`

    4) 不同 UI 系统 ( BBwidget / CWwidget ) 不能被同时支持 

        `隔离` BBwidget( CWwidget ) 与 Ival_box: 放 `同一层次` 

2 接口继承

(1) Ival_box 作 纯粹接口: 不带状态 (数据) & 不定义 vf , 不含 nvf

    user 程序 与 接口类 Ival_box 间 交互代码, `不因` Ival_box 派生类 Ival_slider 的 `改变 而变`

(2) Ival_slider

1) public 继承 自 Ival_box (接口类)

2) protected 继承UI 系统/BBwidget (辅助实现类)

    ——————————————————————————————————————————————————————————————————————————————————
    BBwidget -> Ival_slider 如何选 `继承方式` ? public / protected / private ?
    ——————————————————————————————————————————————————————————————————————————————————          
    [1] private 继承
        
        1] 阻止进一步派生
            派生类 Ival_slider 的 派生类 无法访问 BBwidget
            
        2] 含义: is-implemented-in-terms-of ( 据某物实现出 ) 
    ——————————————————————————————————————————————————————————————————————————————————  
    2] public  继承
        
        is-a -> 含义不对

        `实现类 BBwidget` 提供 `public 的 实现细节` -> 误用 
    ——————————————————————————————————————————————————————————————————————————————————
    [3] protected 继承 
    
        1] 可进一步派生: `派生类 Ival_slider` 的 `派生类 可访问 BBwidget`
                
        2] 含义: is-implemented-in-terms-of ( 据某物实现出 )
    ——————————————————————————————————————————————————————————————————————————————————
    => protected 继承 是最好方式

(3) 多重继承: 实现类 ( BBwidget )

    ——————————————————————————————————————————————————————————————————————————————————
    作 `接口类 Ival_box` 的
    ——————————————————————————————————————————————————————————————————————————————————  
    [1] 基类
                
        依赖
    ——————————————————————————————————————————————————————————————————————————————————  
    [2] 成员
                
        `无法覆盖` Ival_box vf
    ——————————————————————————————————————————————————————————————————————————————————      
    [3] 指针成员: BBwidget*
            
        `额外 forward( 转发 ) 开销`    
    ——————————————————————————————————————————————————————————————————————————————————      
    [4] `平行类: Ival_box 派生类 Ival_slider 的 protected 基类` 
    ——————————————————————————————————————————————————————————————————————————————————
    => 作 `接口类 Ival_box` 的平行类最好

3 灵活变形

(1) `接口类 Ival_box` 派生出 `抽象的派生类`, 与 实现类 BBwidget 同层
    
(2) 实现类 层次中添加 专有类: 优化` 

4 对象创建: Factory - 虚 createFunc

    // 1] 抽象 factory 类
    class Ival_maker                  
    {
    public:
        virtual Ival_dial* dial(int, int) = 0;
        // ...
    };
    
    // 2] 派生 
    class BB_maker: public Ival_maker 
    {
        Ival_dial* dial(int, int) override
        {
            return new BB_ival_dial(a, b);
        }
    };
    
    // 3] 用户 
    void user(Ival_maker& im)
    {
        unique_ptr<Ival_maker> pb { im.dial(0, 99) };
        // ...
    }
    
    // 4] Factory 对象
    BB_maker BB_impl;
    
    // 5] `驱动程序`
    void driver()
    {
        user(BB_impl); // 使用 BB
    }

21.2 多重继承

1 多重接口类 = 多重 抽象类接口 + 不含 可变状态

    可被 `复制/copy` 和 `共享`

2 多重实现类

(1) Derived* 作实参调 paraType 为 Base1* 的 func, func 中 只能看到 DerivedObj 的 Base1 part

(2) 类 B1/B2 为什么要作 基类, 而不是用 成员(B1 b1/B2 b2) 或 指针成员(B1* / B2*)?

答: D 想 override B1/B2 各自的 vf

           B1         B2 
           |\        /|
             \      /
              \    /
                D
             
    void f1(B1*);
    void f2(B2*);
    
    void f(D* pD)
    {
        f1(pD);
        f2(pD);
    }
(3) 意义  

    ————————————————————————————————————————————————————
    优点 
        `无 额外 forward( 转发 ) 开销`
    ————————————————————————————————————————————————————    
    折中
        `不影响 总体设计` 
    ————————————————————————————————————————————————————        
    缺点
        有时会不小心暴露 实现细节 
        
        but, 总体上看, 没有任何技术 能够做到足够完美 
    ————————————————————————————————————————————————————    

3 二义性 解析

(1) `2个 基类 memFunc 同名` 
        |
        |   解决1 
        |/ 
    显式限定 消除 
        |
        |   解决 2: 中间层/封装 
        |/
    `直接` 在 `多重派生类` 中 定义 `同名新函数`: `hide` 基类中 所有该 `同名 实体`     
                        |       
                        | 其中 
                        |/
                    显式限定 消除 

(2) 中间层 BB1/BB2 对 B1/B2NVI (non-virtual interface ) 手法Template Method

1] BB1/BB2 把 B1/B2 的功能映射下来

2] using 声明 继承 Ctor

3] non-vf纯虚 vf -> 延迟 redefined/override 于 D 中

    #include <iostream>
    struct B1
    {
        void f() {};
    };
    struct B2
    {
        void f() {};
    };

    struct BB1 : B1
    {
        // BB1 把 B1 功能映射下来
    public:
        using B1::B1;
        virtual void b1_vf() = 0;
        void f()
        {
            b1_vf(); // <=> this->b1_vf(): 多态
        }
    };

    struct BB2 : B2
    {
        // BB2 把 B2 功能映射下来
    public:
        using B2::B2;
        virtual void b2_vf() = 0;
        void f() { b2_vf(); }
    };

    class D : public BB1, public BB2
    {
    public:
        virtual void b1_vf() { std::cout << "B1: f()\n"; };
        virtual void b2_vf() { std::cout << "B2: f()\n"; }
    };

    int main()
    {
        D d;
        BB1* p = &d;
        p->f();

        BB2* p2 = &d;
        p2->f();
    }

(3) 其中之一用 const: 重载机制区分

4 重复使用 基类: 菱形继承

    1 个 `最远派生类 对象` 包含 `2 个 最远 Base 对象` 
        |
        |   问题 
        |/
    若 最远 Base 含 data, 如何 确保 data 不被 `重复使用`
        |                                   | 
        |                                   |   2个问题 
        |                                   |/
        |       在 最远派生对象 中 `占内存` + `二义性` 
        |
        | 解决
        |/
    `基类` 声明为 `virtual`: 避免重复 
      |                 
      |/

5 虚基类

(1) 虚基类 包含 数据:共享 数据

————————————————————————————————————————————————————————
`类层次` 中 `2个类 共享数据` 的 3 种 明显方式 
————————————————————————————————————————————————————————    
    [1] data 放 `non-local ( global / namespace ) scope`  
        
        破坏 封装性 和 局部性 
————————————————————————————————————————————————————————    
    [2] data 放 `基类` 
        
        单继承: data "冒泡" 到 `public 继承树 的 根部`
            与 non-local 问题一样    
————————————————————————————————————————————————————————    
    [3] 某处 `分配 对象`, `指针` 分别 `交给 2 个类` 
        
        `通过指针(偏移量) 共享数据: 虚继承` 
————————————————————————————————————————————————————————

(2) 虚基类 对象内存布局 : 虚继承机制

Derived 对象 中 的 指针/偏移量: "指向" 共享对象

(3) 虚基类 Ctor/Dtor

1) 虚基类 Ctor:最终派生类 的 Ctor 负责 (显式 或 隐式) 调用

    => `确保只会被 调用 1次` (即使被 提及多次) 

    即 `虚基类` 永远被认为是其 `最终派生类 的 直接基类` 

2) 虚基类 Dtor: 无 Ctor 那样的问题

6 重复基类 与 虚基类

    ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
    (1) 用 `抽象类` 作 `接口`                      |                                            |      
                                                    |       实现                                 |        note
        可选择                                      |                                           |
    ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————      
    [1] `重复 接口类`                                |   public 最远基类 和 public 中间派生类   |  `最远派生类指针 不能隐式转换` 为 最远基类指针       
                                                    |       均 不设为 virtual                    |
        类层次 中 `每提到1次 创建 1个对象`          |                                            |
    ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————      
    [2] `接口类` 设为 `virtual`                  |   `public 最远基类` 设为 virtual             |  `基类 向 派生类` 的 `强转类型转换` (22.1 节) 产生 二义性 -> 易处理 
                                                    |                                            |
        类层次 中 `所有提及` 它的类 `共享1个对象`   |                                            |
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————      

(2) 覆盖 虚基类 vf

D11/D12 均 public virtual 继承 B, 且都 override 了 B 中的 vf, 则 D2 中也必须 override vf, 否则 完整对象 D 上调用该 vf 具有二义性, 无法构建 vtbl

         B
        / \
       /   \
    D11     D12
       \    /
        \  /
         D2
        
实现继承: Ival_box 从哪获取 图形元素? 通过 继承自 UI 系统类( BBwidget ) .jpg 接口继承 + 实现继承.jpg

接口继承 + 实现继承: 4 种版本

接口类 Ival_box 与 实现类 BBwidget 同层.jpg 接口类 Ival_box 派生出 `抽象的派生类`, 与 实现类 BBwidget 同层.jpg 实现类 层次 中 添加 `专有类`: 优化.jpg 以应用为导向 的 概念化 的 接口层次体系.jpg 二义性 之 解决2: `直接` 在 `多重派生类` 中 定义 `同名新函数`-> 显式限定消除 二义性.jpg 二义性 引入: 想 `组合使用` 2个类 `提供的功能` -> 加 中间层: 接口类 提供 "映射层".jpg 二义性 之 解决3 -> 加 中间层: `NVI 手法` 的 `Template Method`.jpg 菱形 非虚 继承.jpg 菱形 虚继承.jpg 虚继承机制: "指针" / 偏移量 "指向" / 定位到 `共享对象 (虚基对象部分)`.jpg 虚基类.jpg 重复基类.jpg `两个 不同的类` 可能 `覆盖 虚基类的 不同 vf `.jpg
上一篇下一篇

猜你喜欢

热点阅读