深入 C++ 实践:如何在完全不改变已有模块架构的情况下,二次封装接口给外部模块使用

往期本博主的 C++ 精讲优质博文可通过这篇导航进行查找:
👇👇👇👇👇
Lemo 的C++精华博文导航:进阶、精讲、设计模式文章全收录
👆👆👆👆👆

前言

前文中我们讲到,如何通过应用 Pimpl 模式进行接口类实现类的分离,来进行接口的设计。

没有读过前文的读者,可以通过这个链接去读下: 《深入 C++ 实践:通过 Pimpl 模式来谈接口类与实现类分离的设计原则

但是,实际业务场景中是多变的,不可能通过一种方式适用所有场景。

现在我们就来看看一种比较常见的情况:
模块层/平台层代码已经完成了功能开发,但是业务比较复杂,或耦合较深,不便于修改现有接口暴露出来给外部模块使用。想基于已有的功能新增API接口暴露出来供外部使用。

那么对于这种问题,我们的解决思路是什么呢?

解决思路

对于这种问题,我们通常能够想到的是通过协议扩展的形式来解决。

通俗而言,怎么做呢?

封装的类与平台内部的类通过协议扩展形式进行关联,即封装的类和内部的类既不是派生关系,也不是通过指针指向的接口类实现类的关系,而是遵守一套相同的协议进行关联。

即:模块初始化时,内部的类通过协议扩展关联封装的类;模块卸载时,内部的类通过协议扩展与封装的类解除关联;

当调用封装的类时,实际上通过协议扩展去找到内部的内,然后进行相关业务逻辑的流转。

我们本着这样的思路来看一段示例代码:

示例代码

#include <iostream>
#include <map>

// 定义协议接口
class IProtocol {
public:
    virtual void PerformAction() = 0;
    virtual ~IProtocol() {}
};

// 内部类实现协议接口
class InternalClass : public IProtocol {
public:
    void PerformAction() override {
        std::cout << "InternalClass action performed." << std::endl;
    }
};

// 协议扩展管理器
class ProtocolExtensionManager {
public:
    void Register(const std::string& key, IProtocol* protocol) {
        protocols[key] = protocol;
    }
    
    void Unregister(const std::string& key) {
        protocols.erase(key);
    }
    
    IProtocol* GetProtocol(const std::string& key) {
        auto it = protocols.find(key);
        if (it != protocols.end()) {
            return it->second;
        }
        return nullptr;
    }
    
private:
    std::map<std::string, IProtocol*> protocols;
};

// 封装类,用户将使用此类,它对内部类进行封装
class WrappedClass {
public:
    WrappedClass(ProtocolExtensionManager& manager, const std::string& key)
        : protocolManager(manager), protocolKey(key) {}
    
    void DoSomething() {
        IProtocol* internal = protocolManager.GetProtocol(protocolKey);
        if (internal != nullptr) {
            internal->PerformAction();
        } else {
            std::cout << "Action cannot be performed, no protocol associated." << std::endl;
        }
    }
    
private:
    ProtocolExtensionManager& protocolManager;
    std::string protocolKey;
};

// 示范如何使用上述类
int main() {
    // 创建协议扩展管理器
    ProtocolExtensionManager manager;
    
    // 创建内部类并注册到管理器中
    InternalClass internal;
    manager.Register("InternalProtocol", &internal);
    
    // 创建封装类,封装内部类的使用
    WrappedClass wrapped(manager, "InternalProtocol");
    
    // 使用封装类来执行操作
    wrapped.DoSomething();
    
    // 注销协议并清理
    manager.Unregister("InternalProtocol");
    
    return 0;
}

在这个示例中,InternalClass 实现了 IProtocolWrappedClass 是用户互动的封装类,它通过 ProtocolExtensionManager 来调用指定的 InternalClass 实例。

当然,上述的封装还不够彻底。通常对于 WrappedClass 而言,设计者会进行隐藏一系列设计逻辑。那么对于这块,可以采用上文中的 Pimpl 模式,进行接口类和实现类的再次封装,进行信息的隐藏。

再次封装

class WrappedClass {
public:
    WrappedClass(ProtocolExtensionManager& manager, const std::string& key)
    {
        APP_I(*new WrappedClassImp(manager, key));
    }

    WrappedClass::WrappedClass(WrappedClassImp& impl)
    {
        APP_I(impl);
    }
    
    WrappedClass::~WrappedClass()
    {
        APP_E(); 
    }

    void DoSomething() {
        APP_D(WrappedClass);
        d->DoSomething();
    }

protected:
    APP_DISABLE_COPY(WrappedClass);
    APP_DECLARE_IMPL(WrappedClass);
};

class WrappedClassImp : public BaseImpl<WrappedClass> {
public:
    APP_DEFINE_GET_IMPL_FUNC(WrappedClass);

    WrappedClassImpl(ProtocolExtensionManager& manager, const std::string& key)
        : protocolManager(manager), protocolKey(key) {}

    void DoSomething() {
        IProtocol* internal = protocolManager.GetProtocol(protocolKey);
        if (internal != nullptr) {
            internal->PerformAction();
        } else {
            std::cout << "Action cannot be performed, no protocol associated." << std::endl;
        }
    }

private:
    ProtocolExtensionManager& protocolManager;
    std::string protocolKey;
};

如此,再次封装之后,更多的信息被隐藏了。

总结

这类问题的解决思路有很多,在此先抛砖引玉出一种供大家参考。如果有更好的方式,欢迎留言讨论。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值