浅谈事件接口的一步步简化

问题描述:

作为一个快二十年的老码农,来谈谈接口的一步步简化。

早期的时候,并没有接口的说法,那是面向对象程序设计出现之后的事情,开始大家都是使用结构化编程方法。

本文就从回调函数开始,捋一下接口的演进过程(这是随文,不当之处敬请谅解)。

回调函数:

为了实现事件的回调,把函数指针交给另一个过程,实现函数回调。那时候的函数指针随处可见,比如线程创建CreateThread和CreateWindow的窗口循环,Hook钩子调用等等,原型大概是这样:

//回调函数的意义--函数指针2006-8-25 10:18
#include <stdio.h>
#include <conio.h>
void system_func( int (*user_func)(int) ) {
	static int first=0; char ch=' ';
	while ((ch=getch()) != 27) user_func (++first);
	return;
}
int myfunction(int wert) {
	printf("... myfunction: system_call_wert =%d\n", wert);
	return (wert);
}
int main () {
	system_func(myfunction);
	return 0 ;
}

消息映射:

再后来进入了对象编程,当处理控件事件的时候,那时候用的最多的大概就是事件映射,以MFC为例,

BEGIN_MESSAGE_MAP(CTextEditorView, CFormView)
 //{{AFX_MSG_MAP(CTextEditorView)
 ON_BN_CLICKED(IDC_ICONBUT0, OnIconbut0)
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

把一个视图中的控件的特定事件与指定的函数指针关联。上面的例子就是把一个id为IDC_ICONBUT0的图标的点击事件,与OnIconbut0处理函数关联起来,当用户点击的时候就上去调用OnIconbut0;

然后在头文件中定义消息函数

 //{{AFX_MSG(CTextEditorView)
 afx_msg void OnIconbut0();
 //}}AFX_MSG

在cpp文件中进行实现即可。

接口类:

不过这样在比较麻烦,代码也啰嗦,一个控件可能需要绑定很多事件。比如按钮事件,可能很多种,按下、抬起、移动、连击等等,如果每个都进行映射就比较多了,所以可能考虑把一个事件绑定到一个类指针,使用类指针实现两个对象之间的互动。

最早使用过atl事件接口,简要例子如下:

控件中创建接口

template <class T>
class CProxy_IAtlComTYADPEvents : public IConnectionPointImplMT<T, &__uuidof(_IAtlComTYADPEvents)>
{
public:
	HRESULT Fire_OnTestEvent()
	{
//...
		return hr;
	}
};

在需要激发的时候fire,类似

Fire_OnTestEvent((int)wParam,CComBSTR(pBuf));

使用的时候把接口的实现交给控件,类似

#include "EventSink.h"
void CClientDlg::OnButton1()
{
 ::CoInitialize(NULL);
 {
  IObjPtr pObj;
  CEventSink *pEventSink= new CEventSink;
  pObj.CreateInstance(__uuidof(Obj));
  pEventSink->DispEventAdvise(pObj);
  //...
  pEventSink->DispEventUnadvise(pObj);
  delete pEventSink;
 }
 ::CoUninitialize();
}

在html中使用

<OBJECT ID="AtlCom" CLASSID="CLSID:91BF3FCF-FC98-4F66-B1A3-09CBEA700108" height="200" width="200"></OBJECT>
<button id="test" value="test" type="button" onclick="ontest()">测试</button>
<label id="countEvent">1111</label>
<script type="text/javascript" language="javascript">
function ontest()
{
   AtlCom.attachEvent('OnTestEvent', OnTestEvent);//
}
var nCount = 0;
function OnTestEvent()
{
   document.all.countEvent.innerHTML = nCount++;
}
    </script>

Java事件接口:

到了java,这一切变得简单

创建一个接口   

public interface IOnItemSelectListener {
        void onItemClick(int act, int type, int nums);
}

创建控件,从外部传入接口,并在适当时候调用接口函数

 

public class MyPopWindow extends PopupWindow {
    private IOnItemSelectListener mItemSelectListener;
    public void setItemListener(IOnItemSelectListener listener) {
        mItemSelectListener = listener;
    }
    private class clickLister implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                 case R.id.btn_ok:
                    if (mItemSelectListener != null) {
                        mItemSelectListener.onItemClick(1, mType, mNum);
                    }
                    break;
            }
        }
    }

在外部使用

      final SectionPopWindow popupWindow = new MyPopWindow(thisActivity, type);
        popupWindow.setItemListener(new MyPopWindow.IOnItemSelectListener() {//使用内联类的方法调用
            @Override
            public void onItemClick(int act, int type, int num) {
            }
        })

lambda接口:

后来,大家发现这种写法太罗嗦,于是出现啦lambda语法,它被很多高级语言使用,比如c#和JAVA,上面的写法使用lambda语法简化如下。

popupWindow.setItemListener((act,type, num)-> { });

调用接口的方法非常多,这仅仅只是一种。

总结:

语言也是在发展,不停的简化,毕竟人们都希望能用最少的语句实现更多的功能,但这毕竟只是愿望,所有的东西实际上都是需要一点点实现的。越是高级的东西内部越是复杂,及高级又灵活的东西那是非常难的。

关于:

编者:李国帅

qq:9611153 微信lgs9611153

时间:2021/12/25  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微澜-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值