`
dcaoyuan
  • 浏览: 307407 次
社区版块
存档分类
最新评论

Erlang里的OO和Java里的OO

阅读更多
[Updated: Feb 18, 2007, 增加了关于Java中静态方法的内容]
首先,这里的OO中的Object仅指包含可变状态的Object,暂不涉及有关OO的多态、继承等概念。

一、Erlang的OO
1、保存在函数调用栈中的状态
Erlang是函数式语言,一般而言,对于事物可变的状态(参数和中间状态)尽可能局限在函数调用中处理完,在调用过程中这些状态全部保存在函数的调用堆栈中,也就是说,函数处理的是事物瞬间的状态。这时,可以说,Erlang不太关心包含可变状态的Object。

2、保存在共享内存或硬盘上的状态
当然,Erlang肯定要处理现实世界的可变状态,这些可变状态,如果是临时的,通常可以以ets形式保存在内存中,如果变化的周期较长,也可以以dts的形式保存硬盘上。ets和dts是erlang自带的数据库机制。这时,可以说,Erlang以一种基本数据结构的角度来看待包含可变状态的Object。

3、保存在Process中的状态
世界上的事物总是意想不到地复杂的,有时,甚至很多时候,上述两种方式都不太好处理事物的状态。典型的情况是UI,UI的组件需要长时间显示在屏幕上,而这些组件的状态通常是非常复杂的,比如,一个按钮,需要保存它的大小、位置、外观、文字、提示、在点击时可能需要做出的动作,等等等等。由于这类状态的持续性、易变性和异步的特点,以Object的方式来处理似乎是最合适的。目前函数式语言普遍对UI的处理比较弱。

对于这种情况,Erlang的方式是:Process就是Object。

Erlang可以把状态保存在一个Process的Loop循环中,Process之间通过Message来交互。以按钮为例:

xml 代码
 
  1. loop(B, Display, Wargs, Fun) ->  
  2.     receive  
  3.     {event,_,buttonPress,X} ->  
  4.         flash(Display, Wargs),  
  5.         Fun(X),  
  6.         loop(B, Display, Wargs, Fun);  
  7.     {event,_,expose,_} ->  
  8.         xDo(Display, B),  
  9.         xFlush(Display),  
  10.         loop(B, Display, Wargs, Fun);  
  11.     {onClick, Fun1} ->  
  12.         loop(B, Display, Wargs, Fun1);  
  13.     {set, Str} ->  
  14.         Win = Wargs#win.win,  
  15.         xDo(Display, xClearArea(Win)),  
  16.         Bin =  draw_cmd(Display, Wargs, Str),  
  17.         loop(Bin, Display, Wargs, Fun);  
  18.     {'EXIT', Pid, Why} ->  
  19.         io:format("Here:~p~n",[{self(),Pid,Why}]),  
  20.         true;  
  21.     Any ->  
  22.         Wargs1 = sw:generic(Any, Display, Wargs),  
  23.         loop(B, Display, Wargs1, Fun)  
  24.     end.  

这段代码中可以看到,按钮的状态Wargs(Windows参数)和Fun(登记的动作)通过递归不断地传递(也是保存在调用栈中),并在接受到message时可以做出相应的改变。这时,Erlang的Object不仅具有了状态,而且还能对message做出反应,并且,它在Process中能长时间地维持。

二、Java的OO
这里想谈的是能从Erlang的OO角度来看Java的OO吗?
我的理解是:尽可能。

抛开在Java中的具体实现,假定我们能把Java的Object实例与Erlang的Process类比,那么我们可以:
1、把Object实例的Public方法调用看作是对外部message的反应(恰恰方法调用在OO的本意中原本就是message);
2、把实例方法的调用看作是把函数绑定到(外加到)一堆状态上,相当于Scope在Erlang中的message loop中的函数;
3、把实例的状态看作是在一个Loop中不断被传递的参数。想象有个隐藏在背后的Loop持有这些状态(作为参数),new操作类比为new一个Loop。
4、Class的静态方法是Erlang中的Module方法,相当于Erlang的message loop外的函数。

当然:
1、JVM中的对象实例并不对应着一个share nothing的Process;
2、想象的Loop也不真实存在。

但是,这种转换至少在想象中是可以成立的。而且,它还隐含着一种可能,就是Java的语法可能不需要太多的改变就可以让这种想象变为现实。就是说,如果将来的某一天,JVM的实现真的把每个实例都实现为一个share nothing的process,而且在背后自动地维持着这么一个Loop,再把方法调用实现为message的传递,那么你现在写的Java程序又何尝不能获得Erlang一样的并发优势呢?

有趣的是,SUN确实这么想过,但是由于对性能的忧虑而至少现在没有去做。而且,虽然有人抱怨Java里的基本类型不是对象,但在我看来,这是对的,因为即使在将来,也不可能为每个int i = 1去new 一个进程。何况,通过box,在必要的情况下还是可以把它看作对象的。

这个,也许只是将来。那么,我们现在能做什么呢?

多了解一些函数式编程的知识,并尽量将其用到日常编程中。

对于Java,至少以下几点是现在就可以开始做的:
  • 尽量将会调用其它实例的语句与只涉及自身状态的语句分开。我现在通常将所有涉及自身状态的语句放在每个方法的前面,将调用其它实例的语句放在方法的最后面。(本文的最后给出了一个例子)
  • 如果一个方法处理的主要是基本数据类型及其容器(list, map等等),那么将它们拿到单独的类中,并设计为static方法。
  • 尽量少些setter/getter,可能的话,用一个完整的例程一次性处理完相关的事务。
  • 如果其它实例中get出来一个对象,尽量只对它进行只读操作。如果要改变其状态,想想又没有其它更好的方法。
  • 如果一个方法可以写成static方法,不妨就写成static方法(可惜java的语法会让static方法签名变得很长)。
最后是一个从我的程序中直接摘出来的一段程序,包括在接触FP之前写的,和最近改的。因为刚学函数编程不久,所以改得不一定合适。

之前:
java 代码
 
  1. public void mouseClicked(MouseEvent e) {  
  2.     /** sinlge-clicked ? go on drawing, or, check my selection status */  
  3.     if (e.getClickCount() == 1) {  
  4.         /** go on drawing ? */  
  5.         if (isActivated() && !isAccomplished()) {  
  6.             boolean accomplishedNow = anchorHandle(p(e));  
  7.             if (accomplishedNow) {  
  8.                 drawingPane.accomplishedHandledChartChanged(AbstractHandledChart.this);  
  9.             }  
  10.             /** always set this is selected in this case: */  
  11.             getChart().setSelected(true);  
  12.         }  
  13.         /** else, check my selection status */  
  14.         else {  
  15.             if (chart.hits(e.getPoint())) {  
  16.                 if (getChart().isSelected()) {  
  17.                     final EditAction action = getChart().lookupActionAt(EditAction.class, e.getPoint());  
  18.                     if (action != null) {  
  19.                         /** as the glassPane is always in the front, so add it there */  
  20.                         action.anchorEditor(drawingPane.getView().getGlassPane());  
  21.                         action.execute();  
  22.                     }  
  23.                 }  
  24.                   
  25.                 getChart().setSelected(true);  
  26.                 /** 
  27.                  * I was just selected only, don't call activate() here, let drawingPane 
  28.                  * to decide if also activate me. 
  29.                  */  
  30.             } else {  
  31.                 getChart().setSelected(false);  
  32.                 /** 
  33.                  * I was just deselected only, don't call passivate() here, let drawingPane 
  34.                  * to decide if also passivate me. 
  35.                  */  
  36.             }  
  37.         }  
  38.           
  39.     }  
  40.     /** double clicked, process chart whose nHandles is variable */  
  41.     else {  
  42.           
  43.         if (!isAccomplished()) {  
  44.             if (nHandles == VARIABLE_NUMBER_OF_HANDLES) {  
  45.                 anchored = false;  
  46.                 accomplished = true;  
  47.                 selectedHandleIdx = -1;  
  48.                   
  49.                 drawingPane.accomplishedHandledChartChanged(AbstractHandledChart.this);  
  50.             }  
  51.         }  
  52.     }  
  53. }  
java 代码
 
  1. public void mouseMoved(MouseEvent e) {  
  2.     if (isActivated()) {  
  3.         if (!isAccomplished()) {  
  4.             if (anchored) {  
  5.                 stretchHandle(p(e));  
  6.             }  
  7.         }  
  8.         /** else, decide what kind of cursor will be used and if it's ready to be moved */  
  9.         else {  
  10.             Handle theHandle = getHandleAt(e.getX(), e.getY());  
  11.             /** mouse points to theHandle ? */  
  12.             if (theHandle != null) {  
  13.                 int idx = currentHandles.indexOf(theHandle);  
  14.                 if (idx >= 0) {  
  15.                     selectedHandleIdx = idx;  
  16.                 }  
  17.                   
  18.                 cursor = HANDLE_CURSOR;  
  19.             }  
  20.             /** else, mouse does not point to any handle */  
  21.             else {  
  22.   
  23.                 selectedHandleIdx = -1;  
  24.                 /** mouse points to this chart ? */  
  25.                 if (chart.hits(e.getX(), e.getY())) {  
  26.                     setReadyToDrag(true);  
  27.                     cursor = MOVE_CURSOR;  
  28.                 }  
  29.                 /** else, mouse does not point to this chart */  
  30.                 else {  
  31.                     setReadyToDrag(false);  
  32.                     cursor = DEFAULT_CURSOR;  
  33.                 }  
  34.             }  
  35.         }  
  36.     }  
  37. }  

之后:
java 代码
 
  1. public void mouseClicked(MouseEvent e) {  
  2.     if (e.getClickCount() == 1) {  
  3.         /** sinlge-clicked ? go on drawing, or, check my selection status */  
  4.         final boolean willAnchorHandle = isActivated() && !isAccomplished();  
  5.         final boolean chartSelected    = willAnchorHandle ||  
  6.                 !willAnchorHandle && chart.hits(e.getPoint());  
  7.         final boolean willBeginEdit    = !willAnchorHandle && chartSelected;  
  8.           
  9.         chart.setSelected(chartSelected);

  10.         /** following will affect other objects */
  11.         if (willAnchorHandle) {  
  12.             final boolean accomplishedNow = anchorHandle(p(e));  
  13.             if (accomplishedNow) {  
  14.                 drawingPane.accomplishedHandledChartChanged(AbstractHandledChart.this);  
  15.             }  
  16.         }  
  17.         if (willBeginEdit) {  
  18.             final EditAction action = chart.lookupActionAt(EditAction.class, e.getPoint());  
  19.             if (action != null) {  
  20.                 /** as the glassPane is always in the front, so add it there */  
  21.                 action.anchorEditor(drawingPane.getView().getGlassPane());  
  22.                 action.execute();  
  23.             }  
  24.         }  
  25.     } else {  
  26.         /** double clicked, process chart whose nHandles is variable */  
  27.         if (!isAccomplished() && nHandles == VARIABLE_NUMBER_OF_HANDLES) {  
  28.             anchored = false;  
  29.             accomplished = true;  
  30.             selectedHandleIdx = -1;  
  31.               
  32.             drawingPane.accomplishedHandledChartChanged(AbstractHandledChart.this);  
  33.         }  
  34.     }  
  35. }  

java 代码
 
  1. public void mouseMoved(MouseEvent e) {  
  2.     if (isActivated()) {  
  3.         if (isAccomplished()) {  
  4.             /** decide what kind of cursor will be used and if it's ready to be moved */  
  5.             final Handle theHandle = getHandleAt(e.getX(), e.getY());  
  6.             if (theHandle != null) {  
  7.                 /** mouse points to theHandle */  
  8.                 final int idx = currentHandles.indexOf(theHandle);  
  9.                 selectedHandleIdx = idx >= 0 ? idx : selectedHandleIdx;  
  10.                 cursor = HANDLE_CURSOR;  
  11.             } else {  
  12.                 /** mouse does not point to any handle */  
  13.                 final boolean mousePointToThisChart = chart.hits(e.getX(), e.getY());  
  14.                 readyToDrag = mousePointToThisChart;  
  15.                 selectedHandleIdx = -1;  
  16.                 cursor = mousePointToThisChart ? MOVE_CURSOR : DEFAULT_CURSOR;  
  17.             }  
  18.         } else {  
  19.             if (anchored) {  
  20.                 stretchHandle(p(e));  
  21.             }  
  22.         }  
  23.     }  
  24. }  

分享到:
评论
5 楼 cookoo 2007-02-19  
dcaoyuan 写道


OO和FP是看待真实世界的两个互补的层面,OO就像粒子,FP就像波。最终,完美的语言也许还是需融合这两种观点,但不是像现在的Ocaml和Scala这样,至少,所有的Object必须是sharing nothing的。


似乎很难有完美的通用语言适合各种任务。Ocaml和Erlang的主要差异在于前者可选mutable数据而后者禁止。Ocaml允许mutable数据很大程度上是出于执行性能的设计要求,而Erlang禁止它显然是为了share nothing以追求高并发性。所以设计目的不同,选择也不同。

不久前Channel 9上Anders Hejlsberg等4个大牛依谈到fp和imperative语言的融合问题,主要就是c#3的设计。Anders认为提供更高抽象机制的同时也要允许人们还能按以前的做法干。这就是另外一种设计选择了。
4 楼 回帖专用 2007-02-16  
javavsnet 写道
对于Erlang中如何保持状态还是不理解,dcaoyuan同学能否用Java来实现类似Erlang中保持状态的机制。比如生产者、消费者问题,用Java实现的话一定有一个容器作为全局变量来保存当前生产出来的产品,由消费者来消费其中的产品。这个容器的内容是不断变化的,如何用Java来实现FP式的编程,也就是函数不产生副作用?关于这个问题我在 http://www.iteye.com/post/190775中问过,没有得到java版的答案。


引用
Producer(Consumer) ->
    Consumer ! {product, self(),"Data"},
     timer:sleep(2000),
    ping(Consumer).

Consumer() ->
    receive       
        {product,Ping_PID,Data} ->
            io:format("Consume Data ~p ~n", [Data]),
            Consumer()
    end.


在Erlang中不是所有的函数都是no-side-effection,使用 receive/send,或者读写ETS/Menisia的函数是有Side effection的.
可以修改一下Consumer
引用
Consumer()->
      receive
           {product,Ping_PID,Data} ->
                Data
      end.
Result=Consumer().
Result1=Consumer().

所谓的no-side-effection就是说只要输入给定那么输出不变.但是这里输入一样,而输出Result,Result1的值是不确定的,取决于向Consumer发送消息的人发了什么内容.

从另外一个层面上来说,状态的产生和副作用的出现其根源来自于并行.如果没有并行,就不会有状态.比如说为何我们总是说I/O有状态,因为I/O连接的两端是计算机和操作计算机的人.CPU的运算与人脑运算是并行的,要使得CPU与人同步交互就必须产生状态和消息.


3 楼 javavsnet 2007-02-15  
对于Erlang中如何保持状态还是不理解,dcaoyuan同学能否用Java来实现类似Erlang中保持状态的机制。比如生产者、消费者问题,用Java实现的话一定有一个容器作为全局变量来保存当前生产出来的产品,由消费者来消费其中的产品。这个容器的内容是不断变化的,如何用Java来实现FP式的编程,也就是函数不产生副作用?关于这个问题我在 http://www.iteye.com/post/190775中问过,没有得到java版的答案。
2 楼 dcaoyuan 2007-02-14  
回帖专用 写道
带有Thread的Object,叫做Active Object.在其他语言中也有实现.
Erlang中Message Loop State,使用的是比较频繁,但是作用远没有那么大.
对于状态变更,Erlang更多的是采用ETS/Mesnia.
从另外一个角度说,OO中的一个对象就是一个带有操作语句的微型的内存数据库.
但是OO这样的内存数据库在Distribution上有着不可克服的困难.
所以我以前说过,说Erlang的数据和代码其实是提倡分离的.
没错,Erlang Way习惯于将数据和代码分离,这样可以解决大部分问题,而且带来了许多的好处。

我现在在考虑Erlang实现一套UI Tool Kit的可能性,Joe对此烦恼好几年了,在他的EW11中,用的是Message Loop State,这才让我去想与Java中Object的语义上的类比。

然后再看Java Swing及其应用的代码,感觉UI程序中许多控件的状态不是一步可决的,就是说,一项操作其最终的结果可能需要异步地与多个控件或者说对象进行Message的交流,而且其行为在中间过程中是不可断的,要具备转移到多个分支的可能性,这时保存中间状态就成了常态。

Joe认为UI应当走一个控件、一个Process的方式,这时Message Loop State就成了适宜的选择。而按照我的理解,至少在语义上,OO的Object是可以接受的,而且在语义上甚至与Message Loop State是可以互译的。

我的开源程序是一个UI任务很重的项目,目前已经完成的近4万行代码中有三分之二是Java2D和Swing的,我发现如果把Object与Erlang作上面的类比,可以指导如何尽量将副作用隔离(将调用其它Object的语句与只涉及自身状态的语句分开),同时也发现,UI的复杂性使得OO的语法相对来说写起来直接些,而且是可以接受的,甚至有其优点。

当然,OO的应用需要小心避开其陷阱,比如继承的滥用和getter/setter。尤其是,如果行为与中间状态没有太多关系,就不应该将其写成对象,而应该像Java的Math包一样,全部写成函数。Java中的Collection包也应该是函数而不是对象。

OO和FP是看待真实世界的两个互补的层面,OO就像粒子,FP就像波。最终,完美的语言也许还是需融合这两种观点,但不是像现在的Ocaml和Scala这样,至少,所有的Object必须是sharing nothing的。



1 楼 回帖专用 2007-02-14  
带有Thread的Object,叫做Active Object.在其他语言中也有实现.
Erlang中Message Loop State,使用的是比较频繁,但是作用远没有那么大.
对于状态变更,Erlang更多的是采用ETS/Mesnia.
从另外一个角度说,OO中的一个对象就是一个带有操作语句的微型的内存数据库.
但是OO这样的内存数据库在Distribution上有着不可克服的困难.
所以我以前说过,说Erlang的数据和代码其实是提倡分离的.

相关推荐

    Erlang-or-java.rar_erlang

    标题中的“Erlang-or-java.rar_erlang”表明这是一个关于Erlang和Java之间通信的示例项目。Erlang是一种面向并发、分布式、容错的编程语言,常用于构建高可用性和高并发性的系统,而Java则是一种广泛应用的通用编程...

    erlang-java聊天

    在IT领域,Erlang和Java都是重要的编程语言,各有其独特的优点和应用场景。本项目“erlang-java聊天”是一个初级水平的示例,展示了如何利用这两种语言进行交互,实现一个简单的聊天应用程序。这个聊天程序可能包括...

    erlang调用java

    `Erlang`和`Java`都是在各自领域有着独特优势的语言。`Erlang`以其强大的并发处理能力和容错性著称,而`Java`则有丰富的库支持和广泛的企业级应用。将两者结合,可以充分利用它们的优势。本文将详细探讨如何在`...

    java php python erlang 千万级内存数据性能比较

    本文将深入探讨Java、PHP、Python和Erlang这四种语言在处理千万级内存数据时的性能差异。 首先,让我们从Erlang开始。Erlang是一种并发性极强的函数式编程语言,特别适合构建分布式、容错系统。在提供的文件"erlang...

    Erlang和RabbitMQ安装包

    Erlang和RabbitMQ是两个在分布式系统和消息队列领域中至关重要的技术。Erlang是一种函数式编程语言,以其并发性、容错性和热代码升级能力而闻名,而RabbitMQ则是基于Erlang构建的一个开源消息代理,用于实现应用程序...

    erlang高级原理和应用PPT

    "erlang高级原理和应用PPT" 这个标题表明了文档的主要内容,即关于Erlang编程语言的高级概念和技术在实际应用中的讲解。Erlang是一种面向并发、分布式计算的函数式编程语言,常用于构建高可用性、容错性强的系统,...

    erlang编程 Introducing Erlang

    "Introducing Erlang"是Simon St. Laurent撰写的一本入门级教程,旨在帮助初学者理解和掌握Erlang的核心概念和特性。 ### 1. 函数式编程基础 Erlang基于函数式编程范式,这意味着程序由纯函数构成,没有副作用。...

    一个我自己学习Erlang的聊天室服务器及客户端代码

    本项目提供了一个使用Erlang编写的聊天室服务器端代码以及Java编写的客户端代码,这为我们深入理解Erlang的并发特性和Java与Erlang的交互提供了实践案例。 一、Erlang聊天室服务器端 1. 并发处理:Erlang的轻量级...

    erlang的timer和实现机制

    Erlang是一种面向并发的、动态类型的编程语言,尤其适合构建高可用性和容错性的分布式系统。在Erlang中,`timer`模块是用于处理延时操作和定时任务的关键工具,它提供了丰富的功能,使得开发者能够优雅地处理时间...

    erlang和rabbitmq.zip

    【标题】"erlang和rabbitmq.zip" 涉及到的是两个关键技术:Erlang编程语言和RabbitMQ消息队列系统。Erlang是一种并发性极强、容错性出色的函数式编程语言,而RabbitMQ是基于Erlang开发的开源消息代理和队列服务器。 ...

    ErlangB和ErlangC计算工具(exe可执行文件+excel两个)

    Erlang B和Erlang C是电信领域中两种重要的流量模型,用于预测和分析通信系统中的呼叫处理能力和拥塞情况。这两个模型由丹麦工程师Agner Krarup Erlang在20世纪初提出,至今仍广泛应用于现代通信网络的设计与优化。 ...

    基于erlang的文件存储

    本项目“基于Erlang的文件存储”就是这样一个尝试,它利用Erlang强大的并发处理能力和分布式特性,为服务端提供稳定的基础架构,而客户端则通过Java的Swing组件提供用户友好的交互界面。以下是对该项目中涉及的技术...

    c# 版ErlangOtp跨平台通信框架(Java版的转译)

    Java和C#虽然都属于JVM和.NET生态系统,但它们之间存在差异,因此,将Java版本的Erlang OTP通信机制转译为C#,可以使.NET开发者更方便地利用Erlang OTP的强大功能。 标签中的"ErlangOtp"是关键知识点,代表了Erlang...

    RabbitMQ3.9.13和ErLang24.2版本

    5. **语言和库更新**:Erlang的新版本通常会包含语言特性的增强和库的更新,这些改进可能会间接影响到RabbitMQ的性能和功能。 在安装这两个软件时,首先需要下载Erlang的erlang_24.2.exe文件,安装后才能运行...

    基于erlang后台,java swing前端开发的qq聊天系统

    本系统是基于erlang开发的后台,java swing开发的前端的qq聊天系统,希望能给初学erlang的人带来一点小小的帮助,具体操作步骤见redme.txt文件 功能如下: 功能: 1.用户登录功能,账号和密码从服务端的mysql数据库...

    linux下erlang22版本和rabbitmq3.7版本

    在Linux环境下,Erlang和RabbitMQ是两个重要的组件,尤其对于消息队列系统而言。Erlang是一种通用的编程语言,以其并发性、容错性和分布式特性而闻名,而RabbitMQ则是基于Erlang构建的一个开源消息代理,用于处理...

    erlang programming

    8. **Erlang与其他技术的集成**:Erlang可以与其他语言如Java、Python等集成,用于构建混合系统。例如,使用Erlang的Ranch和Cowboy库可以构建高性能的Web服务器和API。 9. **实时性与并发性**:Erlang的实时性使其...

    图书:Erlang和OTP实战

    《Erlang和OTP实战》是一本专注于Erlang编程语言和OTP(Open Telecom Platform)框架的专业书籍。这本书深入浅出地介绍了Erlang在分布式系统、并发处理以及高可用性设计中的应用,同时结合 OTP 提供了强大的工具和库...

    Erlang的高级特性和应用

    **Erlang 高级特性和应用** Erlang 是一种高级编程语言,以其在并发处理、分布式计算和高可靠性方面的出色性能而闻名。在国内外,Erlang 已经被广泛应用于各种场景,如广告平台、社交网络、云计算、网络游戏以及...

    rabbitMq和erlang安装包

    rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统。它遵循Mozilla Public License开源协议,采用 Erlang 实现的工业级的消息队列(MQ)服务器,Rabbit MQ 是建立在Erlang OTP平台上。

Global site tag (gtag.js) - Google Analytics