C++轻量级代理模式

举报
dosomething 发表于 2023/12/01 16:01:33 2023/12/01
【摘要】 现有代理模式    C++的代理模式有如下2种:        一、抽象类代理,主要用于通知的场景                 代理接口(虚函数)全部放在抽象类中,后面通过派生抽象类的方式生成实现代理类。                     1、【注册】代理类将自身对象(代理对象)注册到数据源,数据源保存代理对象的引用                     2、【注册】使用方将自身...

现有代理模式

    C++的代理模式有如下2种:

        一、抽象类代理,主要用于通知的场景

                 代理接口(虚函数)全部放在抽象类中,后面通过派生抽象类的方式生成实现代理类。
                     1、【注册】代理类将自身对象(代理对象)注册到数据源,数据源保存代理对象的引用
                     2、【注册】使用方将自身注册代理类中,代理类保存使用方的引用
                     3、【数据返回】数据源更新时,通过“接口映射”调用代理对象的接口
                     4、【数据返回】代理对象检索已注册的使用方,并根据使用方已实现的接口,通知数据
            7a3aa18f34eb4d4ca675f569ce36e5f0.png
        

        二、函数代理模式,主要用于1对1的请求和返回

                代理只实现代理接口,并将代理接口注册到数据源,数据源通过代理接口实现数据下发。
                    1、【注册】代理类将自身函数地址(代理函数)注册到数据源,数据源保存代理函数的引用
                    2、【注册】请求方发起请求,传入参数和回调接口,代理方保存回调接口,并向数据源发起请求
                    3、【数据返回】数据源通过代理函数将数据返回给代理
                    4、【数据返回】代理遍历函数列表,并通过函数地址返回数据
                bbe7b05f244f498997de9e438dce29e6.png
        从上述的两种方案中可以看出两者的使用场景并不一致,代理类主要用于通知类接口,代理函数主要用于1对1的请求。
            优点:
                1、代理方和使用方完全分离,代理方只处理数据不需要处理业务,业务在抛给对应的使用方
            缺点:
                1、使用方和代理方代码过于分散,代码分布广,查找阅读困难,也就是代码不够【内聚】
                2、对于多使用方的场景使用成本高,不利于管理,需专门构建分发模块,通过分发模块将数据分配给对应的使用方
                3、而且存在野指针、空指针风险
                4、对于小场景使用成本高,比如:http请求,每个使用http请求的点都需要实现代理函数
        总结:老的代理模式有老的代理模式的好处,对于大型工程构建是很好的体验,对于频繁固定(数据接口可能会频繁触发)的模式这种很有用处,对于只发生1次的请求,这种模式使用成本有点高,同时存在存在野指针的风险。

轻量级代理设计 

        为此,结合Objective-C的block思想,创建一个简单的轻量级代理模式,std::function<lambda>作为轻量级代理,

        核心:轻量级代理的核心就是一个字“轻”

            1、【注册】使用方调用数据源接口,并将lambda传给数据源,数据源通过std::function保存lambda【std::function是重点】

            2、【返回】数据源更新数据时,遍历已存储的lambda,通过lambda直接将数据传给使用方

            edb917e2af4d4454bec4bd66bf756f1e.png

        针对常规代理模式存在的问题,这里都可以解决

            1、代码集中,使用方和代理方在一块,可以相关输出数据,是一对一的关系,同时不需要一个新的代理模块,代理和业务完全在一起

            2、多使用方时,各使用方独立管理自身逻辑,代码完全分割

            3、代码集中,存在野指针和空指针的风险较小

            4、成本特别小

        举例说明:

            设计数据源原型:

class DataSource {
public:
    static DataSource *getInstance();  // 单例

    void sendRequest(int param, std::function<void(int result)> resultCallBack);  // 请求接口
private:
    void doRequest(int param, int key);  // 处理接口
    void finishRequest(int result, int key);  // 返回接口
private:
    static DataSource instance;  // 单例
    std::mutex m_mutex;  // 锁
    int m_key{0};  // key
    std::map<int,std::function<void(int result)>> delegates;  //  代理列表
};

            数据源实现:

DataSource DataSource::instance;

DataSource *DataSource::getInstance() 
{
    return &instance;
}

void DataSource::sendRequest(int param, std::function<void(int result)> resultCallBack)
{
    int key = -1;
    {
        std::lock_guard<std::mutex> guard(m_mutex);
        key = ++m_key;
    }
    delegates[key] = resultCallBack;
    doRequest(param, key);
}

void DataSource::doRequest(int param, int key)
{
    std::cout << "doing request for key " << key << std::endl;
    finishRequest(param * 10, key);
}
void DataSource::finishRequest(int result, int key)
{
    std::cout << "finish request for key " << key << std::endl;
    std::function<void(int result)> callBack = delegates[key];
    if (callBack) {
        callBack(result);
        delegates.erase(key);
    }
}

            使用方发起请求:

int main()
{
    int outParam = 10;
    DataSource::getInstance()->sendRequest(100, [outParam](int result)->void {
        int addTen = outParam + result;
        std::cout << "request1 result with add ten = " << addTen << std::endl;
    });

    DataSource::getInstance()->sendRequest(100, [](int result)->void {
        std::cout << "request2 result  = " << result << std::endl;
    });
}

        结果:

       

std::function实现轻量级代理的原理:https://bbs.huaweicloud.com/blogs/416972

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。