高层次的模块不要依赖低层次的模块,二者都应该依赖于其抽象
抽象不应该依赖于具体,而是具体应该依赖于抽象
为了更好理解该原则,首先明确以下概念:
-
高层次模块:也叫上层代码,一般可以认为是调用方(客户端)。以系统三层结构(表示层,业务逻辑层,数据访问层)为例子,表示层相对于业务逻辑层来说,就是高层;业务逻辑层相对于数据访问层来说就是高层;
-
底层次模块:也叫作下层代码,一般可以认为是被调用方(提供服务的一方)。如上例子,业务逻辑层相对于表示层来说,就是底层;数据访问层相对于业务逻辑层来说,就是底层。
-
抽象:设计原则/模式中的抽象可以理解为约束/规范,代码中表现为接口/抽象类
-
具体:抽象的具体化,代码中表现为实现类/抽象类的派生
比如:鱼类和鸟类是具体类,动物类是抽象类;游泳类和飞行类是具体类 ,行为类是抽象类;App端签到类和PC端签到类是具体类,签到行为类是抽象类。
系统三层结构:https://blog.csdn.net/weixin_28683065/article/details/119067965
考虑以下案例:实现一个多终端(App、PC、…)签到的系统
实例1:
#include <iostream>
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;
}
上述实例中,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 <iostream>
//接口类
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;
}