前言:

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

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



本文目的:

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



场景设计:

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

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

伪代码:

类 Window{  

Button 

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

}

类 Button

{   

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

}

来看看类图设计:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbHVvdGk3ODQ2MDA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">

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

&lt;监听器模式&gt;在C++ 与 Java 之间实现的差异的更多相关文章

  1. C#和java之间的一些差异与共性

    C#与java之间的一些共性和差异整理 隐藏:与java中的重写几乎一致,但是需要添加new关键字让编译器知道,否则会有警告 虚方法:1.声明为virtual的方法就是虚方法,在子类中使用overri ...

  2. Java设计模式-监听器模式

    监听器模式有三个要素——事件源.事件对象.监听器. 事件源:顾名思义,事件发生的源头,比如点击的按钮,属于被监听的对象: 事件对象:这个经常和事件源混淆,它经常被用来包装事件源,切记,它毕竟是个事件, ...

  3. Java设计模式补充:回调模式、事件监听器模式、观察者模式(转)

    一.回调函数 为什么首先会讲回调函数呢?因为这个是理解监听器.观察者模式的关键. 什么是回调函数 所谓的回调,用于回调的函数. 回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数. ...

  4. java设计模式--事件监听器模式和观察者模式

    监听器模式:事件源经过事件的封装传给监听器,当事件源触发事件后,监听器接收到事件对象可以回调事件的方法 观察者模式:观察者(Observer)相当于事件监听者,被观察者(Observable)相当于事 ...

  5. java设计模式--观察者模式和事件监听器模式

    观察者模式 观察者模式又称为订阅—发布模式,在此模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知.这通常透过呼叫各观察者所提供的方法来实现.此种模式通常被用来事件 ...

  6. java设计模式--事件监听器模式(观察者模式)

    这两个模式实质上很简单,在实际项目中也是非常常用的.但却被有些人说的云里雾里,这里用白话解释一下. 本质上两者都是同一个模式.专业的说法是这样的(觉得绕口的请直接转到白话解释部分,再回头来看下面这几句 ...

  7. Delphi的基于接口(IInterface)的多播监听器模式(观察者模式 )

    本文来自:http://www.cnblogs.com/hezihang/p/6083555.html Delphi采用接口方式设计模块,可以降低模块之间的耦合,便于扩展和维护.本文提供一个实现基于接 ...

  8. 在命令符模式下编译并执行Java程序

    对于Java初学者,建议使用纯文本文件来编写Java程序,并在命令符模式下使用工具程序编译和执行Java程序.使用javac工具编译.java,使用java工具执行.class.(推荐sublime编 ...

  9. 【Design Patterns】监听器模式

    监听器模式 监听器模式其实是观察者模式中的一种,两者都有关于回调的设计. 与观察者模式不同的是,观察者模式中存在的角色为观察者(Observer)和被观察者(Observable) 而监听器模式中存在 ...

随机推荐

  1. 圆角矩形“RoundRectShape”使用详解

    圆角矩形 常用作一些组件的背景 构造函数: RoundRectShape(float[] outerRadii, RectF inset, float[] innerRadii) Specifies ...

  2. sql server中的悲观锁和乐观锁

    https://www.cnblogs.com/chenwolong/p/Lock.html https://www.cnblogs.com/dengshaojun/p/3955826.html ht ...

  3. 2017-3-12 leetcode 167 209 216

    ---恢复内容开始--- 对于每次开机avast喊出的“已经检测到危害”实在忍无可忍了(它只能检测到不能根除很气..)于是重装了系统,回到了win10感觉不赖. =================== ...

  4. 计算机网络自顶向下方法第2章-应用层(application-layer).2

    2.4 DNS:因特网的目录服务 2.4.1 DNS提供的服务 DNS的定义 实体层面看,DNS是一个由分层的DNS服务器实现的分布式数据库 协议层面看,DNS是一个使得主机能够查询分布式数据库的应用 ...

  5. 深入分析setContentView

    在网络层,互联网提供所有应用程序都要使用的两种类型的服务,尽管目前理解这些服务的细节并不重要,但在所有TCP/IP概述中,都不能忽略他们: 无连接分组交付服务(Connectionless Packe ...

  6. Monad的重点

    Monad是非常强有力的概念,在介绍Monad是什么和如何工作的之前,我们应该先确认Monad能解决什么问题.Monad是各种编程问题的的 meta solution,它不是某种特定问题的解决方案,我 ...

  7. WordPress的wordfence插件的设置方法

  8. vue-cli 2.0 常用命令

    一.查询npm版本 npm -v 二.安装npm npm install npm g 三.安装webpack npm install webpack -g 四.安装vue命令行工具 npm insta ...

  9. ESLint 规范项目代码

    ESLint 由 JavaScript 红宝书 作者 Nicholas C. Zakas 编写, 2013 年发布第一个版本. NCZ 以可扩展.每条规则独立.不内置编码风格为理念编写了一个 lint ...

  10. 那些年 IE 下踩过的坑

    1年过去了,换了一个不用兼容IE8一下浏览器的工作了! 1.:before,:after(伪类) 所有主流浏览器都支持 :before 选择器. 注释:对于 IE8 及更早版本中的 :before,必 ...