接口版本控制
- 定义新接口版本:
- 创建新的抽象类接口,命名时体现版本信息,例如
IComponentV2
,新接口继承自旧接口IComponent
,保证原有接口功能的兼容性。
- 在新接口中添加新的虚函数来满足新需求,如
void newFunction() = 0;
。
- 接口标识:
- 可以在每个接口类中添加一个静态成员函数
getVersion()
,返回一个代表版本号的常量,如return 2;
,方便调用方判断接口版本。
兼容性处理
- 保持旧接口不变:
- 原有的抽象类接口及其虚函数的定义和功能必须保持不变,确保现有的模块交互不受影响。任何使用旧接口的代码无需修改即可继续工作。
- 实现类继续继承旧接口并实现其虚函数,以维持原有功能。
- 双接口实现:
- 对于需要扩展功能的模块,实现类同时继承旧接口和新接口。实现类要实现新接口中新增的函数,同时保持对旧接口函数的原有实现。
- 例如:
class ComponentImpl : public IComponent, public IComponentV2 {
public:
// 旧接口函数实现
void oldFunction() override {
// 原有实现
}
// 新接口函数实现
void newFunction() override {
// 新功能实现
}
};
- 运行时动态类型检查:
- 当调用方获取到接口指针或引用时,可以使用
dynamic_cast
进行运行时类型检查。如果检查到对象支持新接口版本,就可以调用新功能。
- 例如:
IComponent* component = getComponent();
IComponentV2* v2Component = dynamic_cast<IComponentV2*>(component);
if (v2Component) {
v2Component->newFunction();
} else {
// 处理不支持新接口的情况
component->oldFunction();
}
模块解耦
- 依赖注入:
- 在模块间传递接口指针而非具体实现类对象,使得模块间依赖于抽象接口而非具体类。这样,即使实现类发生变化(如扩展接口),只要接口不变,依赖该接口的模块就不受影响。
- 例如,在模块A中,通过构造函数注入接口:
class ModuleA {
public:
ModuleA(IComponent* component) : m_component(component) {}
void doSomething() {
m_component->oldFunction();
}
private:
IComponent* m_component;
};
- 中间层抽象:
- 创建中间层抽象类或接口,用于封装模块间的交互细节。各个模块只与中间层交互,而不是直接相互依赖。这样,当某个模块的接口发生变化时,只需要在中间层进行适配,而不影响其他模块。
- 例如,创建
IModuleInteraction
接口,不同模块实现该接口,通过中间层进行交互。
- 使用设计模式:
- 桥接模式:将抽象部分与实现部分分离,使它们可以独立变化。例如,将抽象类接口与具体实现类解耦,当接口扩展时,实现类的修改不会影响到抽象接口的调用方。
- 外观模式:为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。在大型项目中,可以通过外观模式封装多个模块的交互,在外观类中处理接口扩展带来的变化,避免影响到外部调用者。