zoukankan      html  css  js  c++  java
  • <监听器模式>在C++ 与 Java 之间实现的差异

    前言:

            关于各种语言孰优孰劣的讨论在软件界就是个没完没了的话题,今天我决定也来掺和下。

    只是我想探讨的不是哪种语言的性能怎样,钱途怎样。而是站在语言本身特性的基础上中肯地比較探讨。由于如今工作用的是C/C++, 曾经接触过Java,于是我就以这两门语言作为我的对照语言。


    本文目的:

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


    场景设计:

            以监听器模式为例。如今有一个Window, Windows里面有一个button(button有检測点击的函数),当用户点击按

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

    伪代码:

    类 Window{  

    Button 

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

    }

    类 Button

    {   

    检測button被点击函数(){ 若点击,则回调处理函数 }

    }

    来看看类图设计

    来看Java的典型实现:

    /*
    	 * 事件监听
    	 */
    	public interface IOnClickListener{
    		public void OnClicked();
    	}
    	
    	/*
    	 * 按钮类
    	 */
    	public class CMyButton{
    		
    		private IOnClickListener m_listener = null;
    		
    		boolean setOnClickListener(IOnClickListener e)
    		{
    			if(m_listener == null){
    				m_listener = e;
    				return true;
    			}
    			else{
    				return false;
    			}
    		}
    		
    		//这种方法仅仅提供给系统底层调用
    		void click(){
    			if (m_listener != null){
    				m_listener.OnClicked();
    			}
    		}
    		
    	}
    
    	/*
    	 *  Window类
    	 */
    	public class CMyWindow implements IOnClickListener{
    		
    		private String m_strWindowName = "<Defualt Windows>";
    		protected CMyButton m_myButton;
    		
    				
    		public CMyWindow(String strName){
    			m_strWindowName = strName;
    			m_myButton = new CMyButton();
    		}
    		
    		//设置监听器,CMyWindow已经实现了OnClicked,所以事件触发时会回调本类的OnClicked()
    		public void Init(){
    			m_myButton.setOnClickListener(this);
    		}
    		
    		@Override
    		public void OnClicked() {
    			System.out.println(m_strWindowName+"'s button is clicked");
    		}
    	}
    	
    
    	public static void main(String args[]){
    		CMyWindow win1 = new CMyWindow("Win1");
    		win1.Init();
    		
    		/*
    		 * 这里模拟Button点击,事实上应该由其内部
    		 * 底层触发。这里为了方便演示,直接触发
    		 */
    		
    		win1.m_myButton.click();
    	} 


    看看C++採用这样的方式的实现:

    //============================================================================
    // Name        : TestHandler.cpp
    // Author      : 
    // Version     :
    // Copyright   : Your copyright notice
    // Description : Hello World in C++, Ansi-style
    //============================================================================
    #include <stdio.h>
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class IOnClickListener
    {
    public:
    	virtual void OnClicked() = 0;
    };
    
    class CMyButton{
    
    public:
    
    	CMyButton():m_listener(nullptr){};
    
    	bool setOnClickListener(IOnClickListener* e)
    	{
    		if(m_listener != nullptr)
    		{
    			return false;
    		}
    		else
    		{
    			m_listener = e;
    			return true;
    		}
    	};
    
    	//这种方法仅仅提供给系统底层调用
    	void click()
    	{
    		if (m_listener != nullptr)
    		{
    			m_listener->OnClicked();
    		}
    	}
    
    private:
    	IOnClickListener* m_listener;
    
    };
    
    class CMyWindow : public IOnClickListener
    {
    public:
    	CMyWindow(string strName): m_strWindowName(strName){};
    
    	//设置监听器,CMyWindow已经实现了OnClicked,所以事件触发时会回调本类的OnClicked()
    	void Init()
    	{
    		m_myButton.setOnClickListener(this);
    	};
    
    	//@Override
    	virtual void OnClicked()
    	{
    		cout<<m_strWindowName+"'s button is clicked"<<endl;
    	};
    
    	CMyButton m_myButton;
    
    private:
    
    	string m_strWindowName;
    
    };
    
    
    int main() {
    	CMyWindow win1("Win1");
    	win1.Init();
    
    	win1.m_myButton.click();
    
    	return 0;
    }


     C++和Java的代码大致几乎相同。比較显眼的差异在于在Button中 Java採用监听器对象。而C++採用监听器指针。皆因Java的对象能实现多态。 而C++仅仅有指针能够实现多态。


    疑问这里有人有疑问了? 

     这里一个window仅仅有一个button能够通过 CMyWindow 实现监听器IOnClickListener接口OnClicked设置监听器,倘若一个window包含多个button,怎样为每一个button 在CMyWindow 中实现处理函数??OnClicked接口总不能在一个类中被实现多次吧 ~~


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

    简洁的哦~



    public void Init(){
    		
    		m_myButton1.setOnClickListener(new IOnClickListener(){
    			@Override
    			public void OnClicked() {
    				System.out.println("button1 is clicked");
    			}
    			
    		});
    		
    		m_myButton2.setOnClickListener(new IOnClickListener(){
    			@Override
    			public void OnClicked() {
    				System.out.println("button2 is clicked");
    			}
    			
    		});
    	}



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




    //用于定义处理函数的接口
    class IOnClickListener
    {
    public:
    	virtual void OnClicked() = 0;
    };
    
    //实现监听器接口,增加模版指针和类成员指针用于识别  ->回调函数是属于哪个类的哪个方法
    template <class T>
    class COnClickListenerImpl : public IOnClickListener
    {
    public:
    
    	typedef void (T::*OnClickListenHandler)();
    
    	bool setOnClickHandler(T* p, OnClickListenHandler f)
    	{
    		m_callObj = p;
    		m_callBackFun = f;
    		return true;
    	}
    
    	virtual void OnClicked(){
    		(m_callObj->*m_callBackFun)();		//调用类函数指针实现回调
    	}
    private:
    	T* m_callObj;							//回调函数所在的对象指针
    	OnClickListenHandler m_callBackFun;		//回调函数的类成员指针
    };
    
    
    
    class CMyButton{
    
    public:
    
    	CMyButton():m_listener(nullptr){};
    
    
    	template<class T>
    	bool setOnClickHandler(T* p, typename COnClickListenerImpl<T>::OnClickListenHandler f)
    	{
    		COnClickListenerImpl<T>* tmp = new COnClickListenerImpl<T>();
    		tmp->setOnClickHandler(p, f);
    		m_listener = tmp;
    	};
    
    	//这种方法仅仅提供给系统底层调用
    	void click()
    	{
    		if (m_listener != nullptr)
    		{
    			m_listener->OnClicked();
    		}
    	}
    
    private:
    	IOnClickListener* m_listener;
    
    };
    
    class CMyWindow
    {
    public:
    	CMyWindow(string strName): m_strWindowName(strName){};
    
    	void Init()
    	{
    		//为两个按钮分别设置本类的两个处理函数
    		m_myButton1.setOnClickHandler(this, &CMyWindow::OnBtn1Clicked);
    		m_myButton2.setOnClickHandler(this, &CMyWindow::OnBtn2Clicked);
    	};
    
    	void OnBtn1Clicked()
    	{
    		cout<<m_strWindowName+"'s button1 is clicked"<<endl;
    	};
    
    	void OnBtn2Clicked()
    	{
    		cout<<m_strWindowName+"'s button1 is clicked"<<endl;
    	};
    
    	CMyButton m_myButton1;
    	CMyButton m_myButton2;
    
    private:
    
    	string m_strWindowName;
    
    };


    总结:

            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~ 而我们在回调函数里常常还需用到其类的其它属性,所以这里用到类函数指针还是比較方便。






  • 相关阅读:
    任天堂确认账户被黑客入侵:开启双重验证是关键,会更加安全
    受疫情影响!美国大量科技初创企业要挨饿或倒闭
    泰国的IPv6功能已从约2%增至30%,部署率位于全球5名
    vue钩子函数
    vue自定义全局指令directive和局部指令directives
    vue自定义按键修饰符
    字符串padStart、padEnd填充
    vue过滤器
    vue指令v-if和v-show
    vue指令v-for和key属性
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/6949870.html
Copyright © 2011-2022 走看看