• 设计原则五:依赖倒置原则


    高层次的模块不要依赖低层次的模块,二者都应该依赖于其抽象

    抽象不应该依赖于具体,而是具体应该依赖于抽象

    为了更好理解该原则,首先明确以下概念:

    • 高层次模块:也叫上层代码,一般可以认为是调用方(客户端)。以系统三层结构(表示层,业务逻辑层,数据访问层)为例子,表示层相对于业务逻辑层来说,就是高层;业务逻辑层相对于数据访问层来说就是高层;

    • 底层次模块:也叫作下层代码,一般可以认为是被调用方(提供服务的一方)。如上例子,业务逻辑层相对于表示层来说,就是底层;数据访问层相对于业务逻辑层来说,就是底层。

    • 抽象:设计原则/模式中的抽象可以理解为约束/规范,代码中表现为接口/抽象类

    • 具体:抽象的具体化,代码中表现为实现类/抽象类的派生

    比如:鱼类和鸟类是具体类,动物类是抽象类;游泳类和飞行类是具体类 ,行为类是抽象类;App端签到类和PC端签到类是具体类,签到行为类是抽象类。

    系统三层结构:https://blog.csdn.net/weixin_28683065/article/details/119067965

    考虑以下案例:实现一个多终端(App、PC、…)签到的系统

    实例1:

    #include 
    
    class AppClient
    {
    public:
        void signIn() {
            std::cout<<"App签到成功!"<<std::endl;
        }
    };
    
    class PCClient
    {
    public:
        void signIn() {
            std::cout<<"PC签到成功!"<<std::endl;
        }
    };
    
    class User
    {
    public:
        void signInApp(AppClient* client) {
            client->signIn();
        }
        void signInPC(PCClient* client) {
            client->signIn();
        }
    };
    
    class App
    {
    public:
        void appRun() {
            User* user = new User;
            user->signInApp(new AppClient);
            user->signInPC(new PCClient);
        }
    };
    
    int main() {
        App app;
        app.appRun();
        return 1;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    上述实例中,AppClient类、PCClient类、User类、App类都是具体类,没有抽象;按照层次划分,AppClient和PCClient类相对于User类是低层次类,User类相对于App类是低层次类。

    存在的缺点为:

    • App类(高)需要调用User类(低)的方法实现不同签到,如果User类更改(增加或者删除)某些签到方法,App类中也要做响应的更改。此时App类严重依赖User类,违反了原则中的 [高层次的模块不要依赖低层次的模块]

    • 同时,本实例中App具体类依赖User具体类;User具体类依赖AppClient/PCClient具体类,没有抽象类的设计,违反了 [而是具体应该依赖于抽象]

    • User类需求变动时,比如添加了小程序签到,User类就必须变更添加小程序签到的方法。同时违反了开闭原则中的[对拓展开放,对修改关闭]原则

    开闭原则见:。。。。。。。

    因此对上述代码依照设计原则进行修改,将不同的具体签到方式抽象为签到接口类,让User类不再依赖具体的AppClient和PCClient类,而是依赖抽象接口类(签到接口类);同时期望User类是稳定地,不会因为签到方法的增多而改变User代码,实现高层次代码不依赖低层次代码。

    示例2:

    #include 
    
    //接口类
    class ISign {
    public:
        virtual void signIn() = 0; //纯虚函数
    };
    
    //具体依赖抽象
    class AppClient : public ISign {
    public:
        virtual void signIn() override {
            std::cout<<"App签到成功!"<<std::endl;
        }
    };
    
    class PCClient : public ISign  {
    public:
        virtual void signIn() override {
            std::cout<<"PC签到成功!"<<std::endl;
        }
    };
    //add new function 
    class WechatClient : public ISign  {
    public:
        virtual void signIn() override {
            std::cout<<"Wechat签到成功!"<<std::endl;
        }
    };
    
    //稳定点: 高层模块不依赖低层次的模块,二者依赖抽象
    //增加新的签到方法时,不影响User类
    class User {
    public:
        void signIn(ISign* sign) {
            sign->signIn();
        }
    };
    
    class App
    {
    public:
        void appRun() {
            User* user = new User;
            ISign* signPC = new PCClient;
            ISign* signApp = new AppClient;
            //add new function 
            ISign* signWeChat = new WechatClient;
    
            user->signIn(signApp);
            user->signIn(signApp);
            user->signIn(signPC);
            
            //add new function 
            user->signIn(signWeChat);
            user->signIn(signWeChat);
        }
    };
    
    int main() {
        App app;
        app.appRun();
        return 1;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
  • 相关阅读:
    终于拿到了阿里P8架构师分享的JCF和JUC源码分析与实现笔记java岗
    lamda安卓逆向辅助框架
    医学视VQA——综述
    十三、企业开发(5)
    RAPTOR 一种基于树的RAG方法,RAG的准确率提高 20%
    Java:泛型
    什么是istio?
    [java]java读取excel
    Vue的class、style绑定
    MySQL数据库企业级开发技术(下篇)
  • 原文地址:https://blog.csdn.net/u011852872/article/details/126048761