<监听器模式>在C++ 与 Java 之间实现的差异

来源:互联网 发布:qq输入法 for linux 编辑:程序博客网 时间:2024/06/14 03:39

前言:

        关于各种语言孰优孰劣的讨论在软件界就是个没完没了的话题,今天我决定也来掺和下。不过我想探讨的不是哪种语言的性能如何,钱途如何,而是站在语言本身特性的基础上中肯地比较探讨。因为现在工作用的是C/C++, 以前接触过Java,于是我就以这两门语言作为我的对比语言。


本文目的:

        我就以监听器的实现为例演示各自的实现代码,认识下Java与C++的代码风格,看看Java是如何滋润地生活在无指针的环境下,瞄瞄指针在C++中又有如何妙用?


场景设计:

        以监听器模式为例,现在有一个Window, Windows里面有一个按钮(按钮有检测点击的函数),当用户点击按

钮时,Windows能提供一个方法处理被点击的事件。

伪代码:

类 Window{  

Button 

 处理按钮被点击函数(){};

}

类 Button

{   

检测按钮被点击函数(){ 若点击,则回调处理函数 }

}

来看看类图设计

来看Java的典型实现:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.      * 事件监听 
  3.      */  
  4.     public interface IOnClickListener{  
  5.         public void OnClicked();  
  6.     }  
  7.       
  8.     /* 
  9.      * 按钮类 
  10.      */  
  11.     public class CMyButton{  
  12.           
  13.         private IOnClickListener m_listener = null;  
  14.           
  15.         boolean setOnClickListener(IOnClickListener e)  
  16.         {  
  17.             if(m_listener == null){  
  18.                 m_listener = e;  
  19.                 return true;  
  20.             }  
  21.             else{  
  22.                 return false;  
  23.             }  
  24.         }  
  25.           
  26.         //这个方法只提供给系统底层调用  
  27.         void click(){  
  28.             if (m_listener != null){  
  29.                 m_listener.OnClicked();  
  30.             }  
  31.         }  
  32.           
  33.     }  
  34.   
  35.     /* 
  36.      *  Window类 
  37.      */  
  38.     public class CMyWindow implements IOnClickListener{  
  39.           
  40.         private String m_strWindowName = "<Defualt Windows>";  
  41.         protected CMyButton m_myButton;  
  42.           
  43.                   
  44.         public CMyWindow(String strName){  
  45.             m_strWindowName = strName;  
  46.             m_myButton = new CMyButton();  
  47.         }  
  48.           
  49.         //设置监听器,CMyWindow已经实现了OnClicked,所以事件触发时会回调本类的OnClicked()  
  50.         public void Init(){  
  51.             m_myButton.setOnClickListener(this);  
  52.         }  
  53.           
  54.         @Override  
  55.         public void OnClicked() {  
  56.             System.out.println(m_strWindowName+"'s button is clicked");  
  57.         }  
  58.     }  
  59.       
  60.   
  61.     public static void main(String args[]){  
  62.         CMyWindow win1 = new CMyWindow("Win1");  
  63.         win1.Init();  
  64.           
  65.         /* 
  66.          * 这里模拟Button点击,其实应该由其内部 
  67.          * 底层触发,这里为了方便演示,直接触发 
  68.          */  
  69.           
  70.         win1.m_myButton.click();  
  71.     }   


看看C++采用这种方式的实现:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
  1. //============================================================================  
  2. // Name        : TestHandler.cpp  
  3. // Author      :   
  4. // Version     :  
  5. // Copyright   : Your copyright notice  
  6. // Description : Hello World in C++, Ansi-style  
  7. //============================================================================  
  8. #include <stdio.h>  
  9. #include <iostream>  
  10. #include <string>  
  11.   
  12. using namespace std;  
  13.   
  14. class IOnClickListener  
  15. {  
  16. public:  
  17.     virtual void OnClicked() = 0;  
  18. };  
  19.   
  20. class CMyButton{  
  21.   
  22. public:  
  23.   
  24.     CMyButton():m_listener(nullptr){};  
  25.   
  26.     bool setOnClickListener(IOnClickListener* e)  
  27.     {  
  28.         if(m_listener != nullptr)  
  29.         {  
  30.             return false;  
  31.         }  
  32.         else  
  33.         {  
  34.             m_listener = e;  
  35.             return true;  
  36.         }  
  37.     };  
  38.   
  39.     //这个方法只提供给系统底层调用  
  40.     void click()  
  41.     {  
  42.         if (m_listener != nullptr)  
  43.         {  
  44.             m_listener->OnClicked();  
  45.         }  
  46.     }  
  47.   
  48. private:  
  49.     IOnClickListener* m_listener;  
  50.   
  51. };  
  52.   
  53. class CMyWindow : public IOnClickListener  
  54. {  
  55. public:  
  56.     CMyWindow(string strName): m_strWindowName(strName){};  
  57.   
  58.     //设置监听器,CMyWindow已经实现了OnClicked,所以事件触发时会回调本类的OnClicked()  
  59.     void Init()  
  60.     {  
  61.         m_myButton.setOnClickListener(this);  
  62.     };  
  63.   
  64.     //@Override  
  65.     virtual void OnClicked()  
  66.     {  
  67.         cout<<m_strWindowName+"'s button is clicked"<<endl;  
  68.     };  
  69.   
  70.     CMyButton m_myButton;  
  71.   
  72. private:  
  73.   
  74.     string m_strWindowName;  
  75.   
  76. };  
  77.   
  78.   
  79. int main() {  
  80.     CMyWindow win1("Win1");  
  81.     win1.Init();  
  82.   
  83.     win1.m_myButton.click();  
  84.   
  85.     return 0;  
  86. }  


 C++和Java的代码大致差不多,比较显眼的差异在于在Button中 Java采用监听器对象,而C++采用监听器指针。皆因Java的对象能实现多态, 而C++只有指针可以实现多态。


疑问这里有人有疑问了? 

 这里一个window只有一个button可以通过 CMyWindow 实现监听器IOnClickListener接口OnClicked设置监听器,倘若一个window包括多个button,如何为每个button 在CMyWindow 中实现处理函数??OnClicked接口总不能在一个类中被实现多次吧 ~~


得意java说:一个类不能对一个接口实现多次,那我建立多个内部类可以了吧,况且我的匿名内部类可以简单又

简洁的哦~



[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public void Init(){  
  2.           
  3.         m_myButton1.setOnClickListener(new IOnClickListener(){  
  4.             @Override  
  5.             public void OnClicked() {  
  6.                 System.out.println("button1 is clicked");  
  7.             }  
  8.               
  9.         });  
  10.           
  11.         m_myButton2.setOnClickListener(new IOnClickListener(){  
  12.             @Override  
  13.             public void OnClicked() {  
  14.                 System.out.println("button2 is clicked");  
  15.             }  
  16.               
  17.         });  
  18.     }  



奋斗C++说:我没有匿名内部类,可我也有内部类(有类名,跟普通类无异),但我不可能为了每个button新建一个内部类吧? 我有更灵活的方法,就是类函数指针。我只需要在CWindow里面为每个button添加相应的处理方法,方法的参数和返回值跟 OnClicked回调函数一样,另外在监听器接口中加上模版的特性也可以实现。




[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
  1. //用于定义处理函数的接口  
  2. class IOnClickListener  
  3. {  
  4. public:  
  5.     virtual void OnClicked() = 0;  
  6. };  
  7.   
  8. //实现监听器接口,加入模版指针和类成员指针用于识别  ->回调函数是属于哪个类的哪个方法  
  9. template <class T>  
  10. class COnClickListenerImpl : public IOnClickListener  
  11. {  
  12. public:  
  13.   
  14.     typedef void (T::*OnClickListenHandler)();  
  15.   
  16.     bool setOnClickHandler(T* p, OnClickListenHandler f)  
  17.     {  
  18.         m_callObj = p;  
  19.         m_callBackFun = f;  
  20.         return true;  
  21.     }  
  22.   
  23.     virtual void OnClicked(){  
  24.         (m_callObj->*m_callBackFun)();       //调用类函数指针实现回调  
  25.     }  
  26. private:  
  27.     T* m_callObj;                           //回调函数所在的对象指针  
  28.     OnClickListenHandler m_callBackFun;     //回调函数的类成员指针  
  29. };  
  30.   
  31.   
  32.   
  33. class CMyButton{  
  34.   
  35. public:  
  36.   
  37.     CMyButton():m_listener(nullptr){};  
  38.   
  39.   
  40.     template<class T>  
  41.     bool setOnClickHandler(T* p, typename COnClickListenerImpl<T>::OnClickListenHandler f)  
  42.     {  
  43.         COnClickListenerImpl<T>* tmp = new COnClickListenerImpl<T>();  
  44.         tmp->setOnClickHandler(p, f);  
  45.         m_listener = tmp;  
  46.     };  
  47.   
  48.     //这个方法只提供给系统底层调用  
  49.     void click()  
  50.     {  
  51.         if (m_listener != nullptr)  
  52.         {  
  53.             m_listener->OnClicked();  
  54.         }  
  55.     }  
  56.   
  57. private:  
  58.     IOnClickListener* m_listener;  
  59.   
  60. };  
  61.   
  62. class CMyWindow  
  63. {  
  64. public:  
  65.     CMyWindow(string strName): m_strWindowName(strName){};  
  66.   
  67.     void Init()  
  68.     {  
  69.         //为两个按钮分别设置本类的两个处理函数  
  70.         m_myButton1.setOnClickHandler(this, &CMyWindow::OnBtn1Clicked);  
  71.         m_myButton2.setOnClickHandler(this, &CMyWindow::OnBtn2Clicked);  
  72.     };  
  73.   
  74.     void OnBtn1Clicked()  
  75.     {  
  76.         cout<<m_strWindowName+"'s button1 is clicked"<<endl;  
  77.     };  
  78.   
  79.     void OnBtn2Clicked()  
  80.     {  
  81.         cout<<m_strWindowName+"'s button1 is clicked"<<endl;  
  82.     };  
  83.   
  84.     CMyButton m_myButton1;  
  85.     CMyButton m_myButton2;  
  86.   
  87. private:  
  88.   
  89.     string m_strWindowName;  
  90.   
  91. };  


总结:

        Java的匿名内部类对监听器的实现既暴力又简单,没有类名,一个内部类只为生成一个特定的监听器对象, 一个对象对应一个Button,思路比较清晰,印证了java的设计基础是优雅简单,尽量让开发者一目了然,也正是如此,java才有众多fans.

       C++不用通过内部类的方式,而是通过添加成员方法的方式为每个button设置回调处理函数,而中间用到了模版,类函数指针, 通过模板技术构造一个中间监听器模板类,在设置监听回调函数时自动实例化模板类实例的对象,在Button中通过保存接口指针,利用多态性间接地指向实例化的模版类对象, 说起来比较拗口,实现起来稍微复杂间接,但是比较灵活,也从令一方面印证了C++是一门稍微复杂,但是灵活到什么事都可以干。


思考点:

        通过COnClickListenerImpl这个模板类来避免CMyButton成为模版类(若没有COnClickListenerImpl,只能在CMyButton中保存T* m_callbackObj,这样的CMyButton就会变味,跟初衷不同,所以设计要尽量避免修改CMyButton)


疑问: 

 这时候有C基础的朋友可能会迫不及待地说,干嘛用类函数指针,用C函数指针,然后随便定义几个全局函数或者静态函数不就行了(C函数指针在C++不能指向类普通成员函数)?然后在CMyButton中存放一个回调函数指针就行了,连什么OnClickListener这些类都不用定义了~


答疑

 当然,我想说这些方法也是可行的。但是这样的话你的回调函数就不能直接访问CMyWindow的资源了(因为不是类普通函数),想调的话还需要通过其他途径访问CMyWindow~ 而我们在回调函数里经常还需用到其类的其他属性,所以这里用到类函数指针还是比较方便。

0 0
原创粉丝点击