问题描述:
作为一个快二十年的老码农,来谈谈接口的一步步简化。
早期的时候,并没有接口的说法,那是面向对象程序设计出现之后的事情,开始大家都是使用结构化编程方法。
本文就从回调函数开始,捋一下接口的演进过程(这是随文,不当之处敬请谅解)。
回调函数:
为了实现事件的回调,把函数指针交给另一个过程,实现函数回调。那时候的函数指针随处可见,比如线程创建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)-> { });
调用接口的方法非常多,这仅仅只是一种。
总结:
语言也是在发展,不停的简化,毕竟人们都希望能用最少的语句实现更多的功能,但这毕竟只是愿望,所有的东西实际上都是需要一点点实现的。越是高级的东西内部越是复杂,及高级又灵活的东西那是非常难的。
关于: