回调机制

回调机制


在回调机制中,存在三个角色:使用者实现者约定的接口,它们之间的三者关系如下:
回调机制

回调机制 最大的作用就是解耦,通过 约定的接口使用者实现者 之间可以实现解耦;

  • 使用者 不用关心如何实现相关的功能,只需要调用 约定的接口 就行了;
  • 实现者 不用关心如何被调用,只需要按照 约定的接口,实现功能就行了;
  • 约定的接口使用者实现者 之间联系的纽带;

回调函数的应用非常的广泛。通常,我们需要一个统一得接口来实现不同内容的时候,用回调函数来实现就非常合适;许多窗口系统就是使用回调函数来连接多个动作,例如:拖拽鼠标和单击按钮来指定调用用户程序中的某个特定函数。
回调函数的机制优点类似与设计模式中 消费者–使用者模式


C/C++ 常见的回调形式


Callback形式

C 语言中,回调机制的实现是把 约定的接口 的形参设置为函数指针来实现的。下面一个例子用来说明 Callback 的调用形式(例子):这个例子主要模拟的是下载文件;

#ifndef __cplusplus // g++编译器会定义 __cplusplus
#error You are not Currently using a C++ compiler
#endif

#include <iostream>
using namespace std;
// Define Function Pointer TYpes
typedef void (*DownloadCallback)(const char *_pURL, bool _ok);

// Conventional Interface
void DownLoadFile(const char *pURL, DownloadCallback callback)
{
    cout << "downloading ..." << pURL << " " << endl;
    callback(pURL, true);
}

// The Function being called back
void onDownloadFinished(const char* _pURL, bool _ok)
{
    cout << "onDownloadFinished ..." << _pURL << " status:" << _ok << endl;
}

int main()
{
    DownLoadFile("http://www.baidu.com", onDownloadFinished);
    system("pause");
    return 0;
}

在上述例子中,各个函数承担的角色分别如下:

  • 调用者: 主函数;
  • 约定的接口: void DownLoadFile(const char *pURL, DownloadCallback callback)
  • 实现者: void onDownloadFinished(const char* _pURL, bool _ok)

下面我们来看 Callback 应用的另一个例子:在一个链表中,查找指定数据的节点。
链表的数据结构如下:

typedef struct list {
    void *value_address;
    struct list *next;
}Node;

假设,要比较的数据是 int 类型,我们可以这样写:

Node *Search_List_int(Node *_node, const int _value)
{
    while(!_node) {
        if(_value == *(int *)_node->value_address) break;
        _node = _node->next;
    } 
    return _node;
}

假设,要比较的数据是字符串,我们可以这样写:

Node *Search_List_str(Node *_node, const char *desired_str)
{
    while(!_node) {
        const char *str = _node->value_address;
        if(!strcmp(str, desired_str)) break;
        _node = _node->next;
    } 
    return _node;
}

函数 Node *Search_List_int()Node *Search_List_str 实现的功能都是对数据进行比较,唯一不同的是比较的数据类型不一样。如果利用回调机制如何实现一个与类型无关的比较函数呢?
首先,我们确定约定的接口:

typedef int (*COMPARE)(void const *, void const *);
// Conventional Interface
Node *Search_List1(Node *_node, COMPARE _compare, void const *desired_value)
{
    int ret;
    while (!_node) {
        ret = _compare((_node->value_address), desired_value);
        if(!ret) break;
        _node = _node->next;
    }
    return _node;
}

再次,我们实现 实现者

// The function being called back: int type comparison
int int_compare(void const *a, void const *b)
{
    if(*(int *)a == *(int *)b) return 0;
    return -1;
}

最后使用形式如下:

desired_node = Search_List(root, int_compare, &desired_int_value);
desired_node = Search_List(root, strcmp, "abcdefg"); // 有可能会爆出警告,因为 strcmp 的参数类型为 "const char *" 而不是 "void const *"

Sink 的调用形式

Sink 的调用形式就是以 C++ 类的形式来实现回调功能。还是以下载文件为例:

#ifndef __cplusplus
#error You should use c++ compiler
#endif

#include <iostream>

// Conventional Interface
class IDownloadSink
{
public:  
    virtual void OnDownloadFinished(const char *pURL,bool bOK) = 0;
};

// Interface user
class CMyDownloader
{
private:
    IDownloadSink *m_pSink; // interface object

public:  
    CMyDownloader (IDownloadSink *pSink)
        :m_pSink(pSink) { }

    // be using
    void DownloadFile(const char* pURL)
    {
        std::cout<<"downloading..."<<pURL<<""<<std::endl;
        if(m_pSink!=NULL) m_pSink->OnDownloadFinished(pURL,true);
    }
};

// The implementer of the interface
class CMyFile:public IDownloadSink
{
public:
    virtual void OnDownloadFinished(const char *pURL,bool bOK) 
    {
        std::cout<<"onDownloadFinished..."<<pURL<<" status:"<<bOK<<std::endl;
    }

public:
    // Implement a complete function
    void download() 
    {
        CMyDownloader downloader(this);
        downloader.DownloadFile("www.baidu.com");
    }
};


void main()
{
    CMyFile *file = new CMyFile();
    file->download();

    system("pause");
}

在上述代码中:

  • 约定的接口: IDownloadSink
  • 使用者: CMyDownloader
  • 实现者: CMyFile

使用者实现者 只需要各自的实现。


Delegate 方式

class CDownloadDelegateBase  
{  
public:  
    virtual void Fire(const char* pURL, bool bOK) = 0;  
};  

template<typename O, typename T>  
class CDownloadDelegate: public CDownloadDelegateBase  
{  
    typedef void (T::*Fun)(const char*, bool);  
public:  
    CDownloadDelegate(O* pObj = NULL, Fun pFun = NULL)  
        :m_pFun(pFun), m_pObj(pObj)  
    {  
    }  

    virtual void Fire(const char* pURL, bool bOK)  
    {  
        if(m_pFun != NULL  
            && m_pObj != NULL)  
        {  
            (m_pObj->*m_pFun)(pURL, bOK);  
        }  
    }  

private:  
    Fun m_pFun;  
    O* m_pObj;  
};  

template<typename O, typename T>  
CDownloadDelegate<O,T>* MakeDelegate(O* pObject, void (T::*pFun)(const char* pURL, bool))  
{  
    return new CDownloadDelegate<O, T>(pObject, pFun);  
}  

class CDownloadEvent  
{  
public:  
    ~CDownloadEvent()  
    {  
        vector<CDownloadDelegateBase*>::iterator itr = m_arDelegates.begin();  
        while (itr != m_arDelegates.end())  
        {  
            delete *itr;  
            ++itr;  
        }  
        m_arDelegates.clear();  
    }  

    void operator += (CDownloadDelegateBase* p)  
    {  
        m_arDelegates.push_back(p);  
    }  

    void operator -= (CDownloadDelegateBase* p)  
    {  
        ITR itr = remove(m_arDelegates.begin(), m_arDelegates.end(), p);  

        ITR itrTemp = itr;  
        while (itrTemp != m_arDelegates.end())  
        {  
            delete *itr;  
            ++itr;  
        }  
        m_arDelegates.erase(itr, m_arDelegates.end());  
    }  

    void operator()(const char* pURL, bool bOK)  
    {  
        ITR itrTemp = m_arDelegates.begin();  
        while (itrTemp != m_arDelegates.end())  
        {  
            (*itrTemp)->Fire(pURL, bOK);  
            ++itrTemp;  
        }  
    }  

private:  
    vector<CDownloadDelegateBase*> m_arDelegates;  
    typedef vector<CDownloadDelegateBase*>::iterator ITR;  
};  


class CMyDownloaderEx  
{  
public:  
    void DownloadFile(const char* pURL)  
    {  
        cout << "downloading: " << pURL << "" << endl;  
        downloadEvent(pURL, true);  
    }  

    CDownloadEvent downloadEvent;  
};  

class CMyFileEx  
{  
public:  
    void download()  
    {  
        CMyDownloaderEx downloader;  
        downloader.downloadEvent += MakeDelegate(this, &CMyFileEx::OnDownloadFinished);  
        downloader.DownloadFile("www.baidu.com");  
    }  

    virtual void OnDownloadFinished(const char* pURL, bool bOK)  
    {  
        cout << "OnDownloadFinished, URL:" << pURL << " status:" << bOK << endl;  
    }  
}; 

可以使用下面的参数进行设置:

int _tmain(int argc, _TCHAR* argv[])  
{  
    DownloadFile("www.baidu.com", OnDownloadFinished);  

    CMyFile f1;  
    f1.download();  

    CMyFileEx ff;  
    ff.download();  
    system("pause");  
    return 0;  
}

总结:

  • Callback 的方式是面向过程,使用简单灵活;
  • Sink 的方式是面向对象的,在 C++ 中使用比较多,可以在一个 Sink 里封装一组回调接口,适用于一系列比较固定的回调事件;
  • Delegate 的方法也是面向对象的,和 Sink 封装一组接口不同,Delegate 封装是以函数为单位,粒度比 Sink 更小更灵活;

参考


  • 文中部分例子参考网络,因觉得挺好的故没有修改,也找不打最原始的出处,故没有给出链接,望见谅;
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值