JSR 299 – Web Bean

在实验室就总是看一些估计这辈子都不一定用得上的东西。所以基本上3年没碰过JavaEE的我,被发去看JSR 299,然后做ppt……

看了1天多,重新梳理了一遍诸如JSF、EJB3.0、Seam的相关技术,最后算是把JSR 299理顺了。

然后,结论的个人感觉就是,我tm总觉得用Java Web开发这块,这么多年的发展就一直是在为Sun当年一开始搞出来的那个最不靠谱的J2EE体系结构埋单……比如说,当初花了几年的时间,最后用hibernate把那个快把人给弄死了的EJB2搞掉了。而现在其实就是在用Seam搞掉也是相当不靠谱(不得不说Lazy Initialization Exception真是一个奇迹)的Servlet Container和EJB Container的严格界限——merge掉JSF managed bean和EJB session bean(这么多bean,囧……)。

总的来说,最开始的J2EE就是一帮不编程的大佬们说“要有光”,所以最初版本的J2EE最大的用处是方便公司对开发部门进行职能划分。可惜大佬们话一出工业界就开始大量投资给“光”,于是即使发现“光”很靠谱也不能推倒重来,只能在现有基础上慢慢改。加上JCP这个官僚系统,基本上就是现在这个结果了。

ok,完全是自说自话,不能寄希望我用2天就搞清楚java近2-3年的发展。

技术关注贴

两个有意思的事,

Web Client Software Factory发布了。一直挺关心这东西,做得好的话,可能是另一个Killer级的东西。但是目前机器上没跑.Net,找了半天也没找到一个图文并茂的例子,汗……莫非太新了……

Rails的1.2版本也Release了。不过我土土的刚发现了一个1.1中就有的特性——引入了rjs。对js恐惧者来说是很好的特性,但自从学会js之后我就不太喜欢这种封装了。我承认这种封装一种趋势,类似dao层对sql的封装。问题是现在对ajax的理解还不成熟,所以一开始就封装得这么狠,可能是不好的。另外1.2中有意思的就是加入了对REST的支持。

Google Web Toolkit一瞥

http://code.google.com/webtoolkit/,用Java的方式来写Ajax程序。其实仅从语言的角度讲,我很喜欢js,动态类型、不封闭的类、FP等等。但是考虑到js的浏览器兼容性的问题和配套IDE等等的支持的话(虽然不可否认用FF之后js开发轻松了很多,但是那个倒霉IE)……。

一组跨平台的js ui,非常希望google能把这组ui脱离GWT放出来。开发方式,看看名字就行了(想想SWT),控件、布局、事件监听。Java的好处也全有了,类型检查、Eclipse、Junit,不过可以想象少了纯js的一些动态快感。另外就是applicationCreator的方式挺Rails的,呵呵

如果这个东西成为事实标准的话,这将是一件非常非常奇妙的事情。简单的看我们又多了一个纯java的基于Ajax的RIA解决方案,但事情比这个复杂。因为其个东西被google推出来是很奇怪的——google很有影响力,但是他不是java社区的一分子。他是想趁机混进去,还是纯粹玩玩呢?可能比较短视的看就是IBM等Java厂商对Ajax的有点漠不关心的态度(也许SOA牵扯了IBM等等太多的精力了,也没准它更希望Laszlo或Eclipse RCP,反正对比M$的Altas,我们发现Java世界没有很厂商支持的Ajax方案)让google决定自己来干——google在所有的RIA里肯定最喜欢ajax。而我第一眼看见这个就已经觉得它赢了Laszlo和Eclipse RCP了(类Laszlo、Eclipse RCP的方案里我支持M$的Avalon。但是我觉得更可能的是他们全输,因为没办法推广,OS和AS厂商的对立注定了这种结果,你能让想象Servlet输出XAML或者Windows预装Eclipse吗?)。但是如果IBM又后悔了的话,那也许我们又会面对一个分裂的世界了。

疑问是google打算投入多大的精力来维护GWT,比如我们需要Eclipse GWT Plugin,但是同样可以看到其实真正漂亮的Eclipse Plugin还是需要基金会支持的,但是这是一个来自google的方案。事实是google之前还没有掺入java社区的先例。

PS:想法挺乱,真的觉得Java社区应该有一个厂商支持的类似的东西。也许这个东西就会像其他Ajax Framework一样不温不火,纯粹是我高估了它,难说,呵呵

SCA

SCA的概念没有任何难以理解的,最主要的就是一个Service Oriented Spring,至于传输协议的透明性,那是runtime的附加能力;另外支持多语言这件事,如果C++能跑到JVM上就另当别论了,呵呵。有趣的是这两天看Celtix是发现它提到自己和Apache Tuscany整合的问题,结论是总把ESB画成一个长方形是有误导嫌疑的。

SCA的Module是可以compsite的,所以如果想象BPEL和Java的SCA Module(我的意思的Module里Component的主要成分)在不同的层次的话,那么这里就对应了比较被经常提到的SOA系统的层次。

蛮喜欢这个东西是因为是很Best-practice,比如不透明的对待local和remote,再比如IoC。略微不喜欢的就是这种通过一个规范来实现多语言版本Component的方式,COM的失败虽然和这个方式没关系,但总是不好的印象。还有从政治上说BEA和IBM联合地搞JCP也不是什么好事(虽然也未必是坏事)。

另外值得一提的就是M$的Indigo,粗略的印象里,Indigo(在CLR的帮助下)解决了SCA和SDO里除了wire的全部问题。M$一向对模式架构不关心,所以没有一个官方版本的IoC容器不值得奇怪。但是如果Indigo就是应用了OS能力的library的话,那么加一个NSpring就足以比拟SCA了

python的垃圾回收机制

因为要和别人讲理,所以把这部分代码看了看。

大概说一下我的理解。python广义上的垃圾回收是用两种互补的方式实现的:首先对于每一个对象,如果它的引用计数减到0,那么它的__del__会先被调用然后被回收;python里还有一个gcmodule,这个模块虽然叫gc,但其实只是处理循环引用的问题。注释中明确地说了这个gcmodule中不会发现引用计数为0的对象。这个模块中有分代的机制。

比较java/C#和python的垃圾收集(也许是内存管理)大概有两方面区别。python和java/C#第一个区别是垃圾回收的时机。python是一旦一个对象的引用计数减为0就把这个对象回收,这里实际上是延续了C/C++的思维方式(考虑new/delete)。而java/C#都是在内存紧张才会执行整个空间上的垃圾回收。这里python看上去的好处是__del__的调用时机很明确,但是如果深入观察就会发现其实不是这样,因为你没办法控制引用计数,一旦发生循环引用你依然没办法控制__del__调用的时机(根据源码__del__还是有可能根本不被调用)。我是很怀疑python在这里把可以一起做的事情拆散了,因为完全可以在gcmodule处理循环引用的时候一并处理引用计数为零的情况。

另外的区别就是对堆的使用方式。java在创建对象时用的是很简单的方法就是从前向后不断分配――很想栈所以很快,这样在垃圾收集时,显然必要地,会对回收后还在使用的内存进行压缩。而python(至少是CPython)是继承了C的malloc,我对malloc的机制不怎么熟悉,印象中应该是空闲块list的方式吧,可能不对。加之前面回收时机的问题,这就使得python很需要一个高效的pool。

以上两种综合的机制(立即回收+malloc vs. 必要时回收+像栈一样使用,实际上有点像C/C++ vs. java了)哪个效率更高肯定是根具体实现有关的,要看具体的数据结构和算法(回收算法,pool的实现)。不过如果是java/C#对比python,那么java/C#作为大厂商们的推崇,胜出应该必然的结果。

学习了一下Python的实现

最近在网上看到这个《Python源码剖析》系列,写得很好,赞一下顺便帮着宣传http://blog.donews.com/lemur/,呵呵

顺着看下来,发现Python的实现的确有够糟糕。又引用计数来维护对象进行垃圾回收,每当一个对象的引用计数减到零时就进行回收――我怎么也觉得这是十年以前的技术了。

另外比如对于int和string类型(都是经常被大量创建和销毁的对象)使用了两种不同的缓存机制来加速。但是对于int来说,只有一定区间内(官方版本里是[-5,100)之中)的整数才能被缓存,而区间之外的整数来说都会被创建多个不同的副本。而对于string则是虽然被缓存再利用但是缓存之前字符串一定要被创建一次――也就是说这个机制只能帮助只是帮助比较操作(估计是没说清楚:-p)。本能上觉得应该有更好的做法,但也未必,因为我实际上也就看过这一种实现。

再有的发现就是一个PyPy项目http://codespeak.net/pypy/dist/pypy/doc/news.html,用Python来实现Python。从用Lisp实现Lisp开始,我就没有搞清楚这里的逻辑关系,抽空要把这个项目看一看。

程序设计语言中的动静融合

 

程序设计语言概论课的作业,1万多字,其中可能有8000多我自己写的吧。贴在这里,把摘抄不是自己写的部分删掉只留标题,所以有些段落可能不够通顺,姑且看之。Word贴过来,格式也许有问题

——————————————————-

动态语言与静态语言概述

我们常用静态语言和动态语言来划分众多的编程语言,其实这种划分并没有明确的界限。相对明确的概念是语言静态特性和动态特性的划分。动态特性是指在运行时刻的语言特性,主要依赖解释器或虚拟机,而静态特性则依赖于编译器。

(对于语言来说,动态这个词在不同的语言中有着不同的表现。对比多种编程语言我们会发现在,某些语言中,大部分关系是在运行之前确定的,即在编写代码,编译或者连接的时候就已经确定了――也就是说静态特性多一些;另一些语言则尽可能地推迟确定的时间,直到运行时才确立许多关系――动态特性多。较多采用静态特性的语言比较倾向于静态,反之就比较倾向于动态。任何一种编程语言都可以看作具有某种程度的动态特性,比如最简单是一个变量可以运行时改变它的值,可以说它也具有动态特性。而Smalltalk和Lisp这样的语言因为可以运行时改变自身的结构甚至是函数(方法)的定义,我们认为这个语言具有更强的动态特性。)――注:这段应该是抄自《程序员》,删了就不通了,留在这里

早期的程序设计语言(这里主要指高级语言)偏向于极端比如Fortran极端静态,相对的Lisp极端动态。然而下面我们会论述随着程序设计语言的发展,纯粹的静态语言和纯粹的动态语言间有一种相互吸收彼此特性的趋势。特别是随着Java之类的同时包含大量动态特性和静态特性的程序设计语言的出现,使动态语言和静态语言的界限愈发模糊。

(从Fortran -> C/C++ -> Java/C# -> Smalltalk -> Python/Ruby -> Lisp,动态特性从左到右不断加强。比如最原始的Fortran语言中没有堆栈,因此无法在运行时动态分配内存,甚至都无法进行函数递归调用;C/C++进一步,拥有堆栈,指针,能够灵活的在运行时动态生成对象(new语义)。Java和C#语言因为拥有很强的RTTI 功能,使得运行时动态识别、加载和管理类的能力大大提升。使得他们动态特性又前进了一大步。而Python和Ruby干脆可以在运行时动态改变类或是实例的结构或是定义。)――注:这段同上面注

这里我们依照习惯将Java、C#划分为静态语言,而别它们更动态的语言划分为动态语言。由此给出动态语言定义:可以在运行时刻改变变量类型和修改程序结构的程序设计语言,这里运行时刻改变变量类型主要指动态类型特性,而运行时刻修改程序结构主要指可以动态创建、删除类或函数并且可以在类中添加、删除方法或属性以及修改方法的实现;不满足这个的既是静态语言。以下我们首先讨论对这个定义中明显区分静态、动态语言的特性:静态类型和动态类型,并且简述一下静态语言和动态语言的特点;然后论述程序设计语言发展中的动静融合趋势。

静态语言

静态类型简述(删)

静态语言的特点(删)

动态语言

动态类型简述(删)

动态语言的特点

语言动态特性可以为程序提供很多好处,比如:健壮性、可移植性等等,但随着Java吸收了这些特性后,这些好处也不再是动态语言所特有的。在下一节我们会逐一讨论Java所吸收的动态特性和它们所带来的好处。而这里我们主要考虑我们定义的动态语言的特点:动态类型和运行时刻修改程序结构,给程序设计和编写所带来的好处

动态类型最基本设计哲学在于赋予程序设计最大灵活性,不在编译期给程序添加不必要限制,并认为这样可以最大程度的提高开发效率。为了说明这点,我们可以参考一下MFC的实现。MFC(出于效率考虑,但不一定必要)没有使用在现代图形界面中经常使用的多态技术――多态显然是语言动态特性,而是采用一组宏来构造消息链和消息映射等等机制。不可否认MFC从实现来看还比较优雅,但大量的宏使用使得不通过wizard来手工构造一个MFC应用几乎不可能,而且不经过深入研究很难理解MFC的工作方式。对比之下,类似wxWindows之类的图形界面框架使用多态来完成消息映射,其开发和理解难度对比MFC都很低。虽然MFC使用宏来实现主要是出于效率考虑的结果,但这恰好反映出动态语言和静态语言使用是在开发效率(程序设计的灵活性、简单性)和运行效率权衡的结果。

Java等语言吸收了大量动态特性并且被广泛使用后大大提高了人们的开发效率,但由于没有我们定义的动态语言所具备的两个主要特性:动态类型和运行时刻修改程序结构,还是给人们的开发工作带来一定的限制。作为例子我们可以参考Hibernate和Ruby on Rails中的O/R Mapping模块。如今主流的O/R Mapping框架因为需要实现对领域模型进行透明的持久化,所以需要在运行时刻对领域模型进行监视,当其数据发生变化时表示“脏数据”。然而为了不在ORM框架和领域模型之间造成不必要的耦合,也就是希望领域模型可以以POJO来实现,这就需要在运行时刻修改领域模型的结构,在其中添加必要的监视“脏数据”的方法。这里Hibernate使用CGLIB+反射机制来实现这种修改,不论从实现还是理解的难度都比Ruby那种从语言层面直接支持的方式差了许多。而实际上Ruby on Rails正是因为其大量动态特性――主要就是动态类型和运行时刻对程序结构的修改――的运用大幅度提升了开发效率才造成了对J2EE社区的极大地震动。

Java――成功的动静融合

从程序设计语言出现开始,静态语言一直是工业界的主流(Fortran、C、C++)――相对来说动态语言则是学界和Hacker的最爱(Lisp、Perl、Smalltalk)――这主要是出于效率考虑的原因,动态语言大量的运行时刻特性造成其效率不彰,在硬件比较慢的时代这是必然的结果。然而随着硬件在摩尔定律的推动下飞速前进,加之软件复杂性越来越高,那种注重运行效率而忽略开发效率的程序设计语言既失去了依存的环境也不能满足人们的需要。长期以来静态语言一直在逐渐吸收动态语言的特性:递归的引入;动态分配内存;多态等等,但这种步伐一直比较缓慢。(其中兼容性也起了一些负面的影响,就因为在对C的兼容性和效率方面投入了极大的关注,一堆天才们也只能设计出C++这种庞杂的语言,而后来的经验表明与其关注语义方面的兼容性倒不如通过对遗产系统的桥接来得直接,也更能卸掉包袱。)

对比后来的语言我们就会发现最后一个广泛流行的比较纯粹的静态语言C++太复杂了,以至于很少人可以正确使用它。即使只是以一定的运行效率的牺牲就来换取不用专家就可以实现绝对不会内存泄露的程序,我想在95年那个时候也会有很多人来做这个交易,何况动态特性还可以提供很多其它有助于开发效率的特性。所以Java的出现具有一定的必然性。

如果说语法是语言的皮,那么拔掉这层皮之后,实现看不出Java和C++有哪一点相似――Java和Smalltalk倒是更像一些――但Java在推出的时候仍然说自己是C++的更新换代,可以说Sun在这一点上做的很好,有一种假的兼容性吸引了广大C++程序员的目光。

根据我们前面的定义Java仍然是一种静态语言,但实际上Java(包括后来微软的Java实现C#、VB.NET)更像是一种动静混合的语言。它对语言动态特性的吸收可能比历史上所有静态语言吸收的综合还要多,下面我们来逐一阅览当时Java所标榜的特性,考察它们是动态或静态特性,并讨论对Java流行的影响。

我们将根据著名的”Core Java 2(7th) “中The Java “White Paper” Buzzwords部份来论述Java的特性:
简单:在做语言繁简的对比时,Java语言选取的对比标准一般是C++,对比后我们会发现我们不再有指针、头文件、结构、联合体、运算符重载(虽然必要)、虚基类、多重继承等等繁杂的概念了,Java真的很简单。遗憾的是同时我们也会发现几乎所有语言都比C++简单。另外虽然简单性不是从动态、静态角度上的特性,但简单而具有强大的表现力长久以来就是动态语言所追求的(相比动态语言Java那种每次从容器中取数据都要经过向下类型转化也就显得不那么简单了)――动态语言的始祖Lisp只用7个操作符就描述了自身。
纯粹的面向对象:Java的流行原因之一,这点的确和动静特性没关系……
分布式:Sun力推的特性,但事实证明同构平台下的分布式操作在绝大多数情况下并不需要,EJB的失败见证了这一点,而人们记住的就是Martin Fowler分布对象设计第一定律:“不要分布你的对象”。这点不是动态、静态角度上的特性,但也没给Java语言的流行带来帮助――如果不是起了反面作用。
健壮性:Java程序比C++程序更健壮,其中动态类型检查和虚拟机的内存管理量的特性功不可没――不会出现C++中的因为向下强制类型转化引发的奇怪错误,更是在理论上彻底消除了内存泄露(前提是虚拟机实现正确)。这是促使Java流行最重要的特性之一――使开发人员更关注于业务逻辑而不是时时刻刻想着堆栈――但其实动态语言从一开始就拥有这两个特性,在Lisp、Perl等等动态语言中从没见过谁关心过内存。(Lisp根本就不关心谁是冯•诺依曼)
安全:部分程度是上一点的附加特性,类似“栈溢出”等等的攻击手段被彻底杜绝了。
体系结构中立:与C++不同,Java代码被编译成byte-code,运行在虚拟机中。虚拟机有各种体系结构下的实现。但是这种体系结构中立对Java的流行有多大帮助很难说。如果说在Java刚出现C/S架构还在当道,那么Client的“一次编写,到处运行”很重要,但等到Java真正被广泛接受的98、99年,第二次浪潮已经来到B/S成为主流。所以很难说这种体系结构中立对于如今主流的Java应用还有什么帮助,可能的也就是嵌入式开发了。当然不论如何,动态语言一直是体系结构中立的,因为整个Java虚拟机的想法就是从动态语言这里发展来的,虚拟机本身就可以算是语言的动态特性。
可移植性:同上,只不过体系结构中立的同时还是OS中立(值得注意的是现在的J2EE应用已经被绑定在应用服务器中了)同样这种可移植性也是一种依赖虚拟机的动态特性。
解释型:毫无疑问,动态特性。不过对Java流行没有帮助。
高性能:(我没有考证这是从第几版开始被写入Java “White Paper”的,如果是一开始就被写入的,那么这就是个彻头彻尾的谎言。)原文的意思是说可以在运行时时刻根据程序运行的硬件环境对程序进行优化。首先可以肯定的是这也是个依赖虚拟机的动态特性;其次如果我们可以在每次部署的时候根据部署平台重新编译系统,那么可以肯定Java的byte-code肯定是要比经过编译优化的二进制代码慢的。可能有人会觉得做不到部署时重新编译,但要注意Java目前的主流应用是B/S架构的企业级应用,所以部署时重新编译是可能的;第三是这里Java有一个很大的贡献,Java的流行使虚拟机的效率在短期内大幅度提升(借助JIT等技术),并向人们证明了解释型语言的运行效率并不是想象的那么可怕。
多线程:提供并发的关键字,由虚拟机支持。较新的动态语言(Python等)都具有。
动态:主要包括动态类装载、反射,比如Java一个类的结构是由解释器在类动态装入时决定的,而在C++这样的静态语言中类在内存中的结构是编译时刻确定的,所以在类中增加一个实例变量或一种成员函数后,引用该类的所有子类都必须重新编译,否则将导致程序崩溃。这里Sun本身也承认Java是一种比C/C++动态得多的语言。原文如下”In a number of ways, Java is a more dynamic language than C or C++. It was designed to adapt to an evolving environment. Libraries can freely add new methods and instance variables without any effect on their clients. In Java, finding out run time type information is straightforward. “

总结一下我们会发现真正对Java流行起作用的语言特性(排除了分布式),除了纯粹面向对象特性(实际上打了折扣,模块、类本身和函数都不是对象,不能以一致的方式来处理,比Smalltalk、Python弱了一筹)之外,Java标榜的所有特性都可以被归为动态特性,而这些特性大部分依赖虚拟机。

如果我们对比一下主流的动态语言Perl、Python和Ruby就会发现Java除了具有静态类型特性和不能在运行时刻修改程序结构之外――后者主要指不能动态改变类、方法的声明等等,这其实是静态类型的一个副作用,比如静态类型提供编译时刻检查使程序不能调用类中还不存在的方法(实际上通过反射和CGLIB扩展字节码可以实现运行时刻一定程度修改程序结构但这种模拟方式很迂回,也不够灵活)――从哪方面看都很像一个动态语言,但也就是静态类型这一点使Java仍然是一个极为动态的静态语言。

Python、C#――进一步的动静融合

从前面所述我们可以看到,Java以静态语言为依托同时大量吸取了动态语言的优势――加之成功的运作――使其在数年间迅速发展为最为流行的程序设计语言之一。

然而自从Java诞生以来――也许是受了“Java是一种简单的语言”这句话的桎梏――从1.1到1.4 Sun除了对Java语言中的瑕疵修修补补(优化虚拟机、修改类库中一部分不好的实现等等)以外,在语言核心层面一直缺乏大的动作。只是到了C#“兵临城下”之际才在Java 5.0(这里Sun的心虚一如这个版本号)加入了泛型、自动拆装箱、元数据等特性,而这些特性也都是已经在其他语言中广泛使用的了。

Sun公司在Java发展历程中的表现告诉我们,这家公司在企图有所创新时几乎总会失败(早期Java类库的实现,Entity Bean等等),而它在软件商业领域的表现更是让我们质疑它的能力(应用服务器、集成开发环境方面的表现,我们有理由认为作为Java的发明者Sun实际可能上并没有从Java的流行中获得了太大的利益),加之JCP这个看似民主的过程存在着其他民主过程的通病――各个大利益集团相互掣肘导致的缓慢,这些因素本来完全有可能让Java这颗新星迅速陨落。但幸运的是Java发展的早期那种对微软的抵抗态度,或者说它所标榜“自由”的“宗教信仰”拯救了它自己。开源社区蓬勃发展,以及其对Java语言的大力支持,使Java仍然是今天最值得使用语言――试想如果失去众多的优秀开源产品(Eclipse、Hibernate, etc),而这些产品支持的是另一阵营(PHP、Python甚至是.NET),那将是怎样一种局面(虽然我不否认偶然中的必然性)。但需要明了的是Java在语言层面的革新动作是始终微缓,而且不值得人们报以期望。而通过下文我们会发现仅仅通过不断扩展的类库是不足以(至少是不能直观的)适应语言发展变化。

这里我们首先指出我们认为语言的发展趋势会是进一步动静融合,而从前面对Java的分析可以看到Java和主流动态语言最大的不同就是静态类型和动态类型,所以进一步动静融合的重点就在于兼取静态类型和动态类型的优势。下文将通过对Python和C#语言目前演变趋势的描述来说明这种趋势。

Python,静态类型的引入

05年年初Python的作者Guido在其blog上发表了数篇文章(Adding Optional Static Typing to Python, etc)论述了在Python中引入可选静态类型的可能性,而后在Python3.0 Plans的文档中Core language部分出现“Add optional declarations for static typing”的目标。下面我们首先叙述一下这种特性,然后讨论其引入带来的变化和好处。

Guido在其blog的文章Optional Static Typing — Stop the Flames中说明Python可能添加4种语言特性来支持可选静态类型,依次是:
参数、返回值类型定义,Argument and return type declarations
属性类型定义,Attribute declarations (maybe)
接口定义,Interface declarations
基于契约设计,Design by contract (maybe)

实际上我们注意到在目前的版本中Python类型判断运用的实际上是一种被戏称为Duck Typing的方法――Duck Typing的名字来自这句话“If it walks like a duck and talks like a duck, it must be a duck”。例如在Python中我们定义一个函数f
def f(i):
    print i.value
这样当我们调用f时传入的参数可以是任意类型,但如果运行时刻发现i不包含value的属性(it is not a duck),解释器就会抛出一个异常。这种动态类型为Python语言提供了极大的灵活性。

但是这种灵活性不是没有代价的,强大的动态类型也使Python丢失了大部分静态类型语言所拥有的便利,这包括:
文档生成,类型可以帮助我们更确切的描述参数和返回值,而动态类型导致需要大量文字来说明
IDE的帮助,现代IDE提供强大的智能完成和代码生成功能,这经常需要变量有确定的类型
优化,编译器可以利用类型信息来进行优化
反射类型信息,通过反射可以在运行时可得到函数参数和返回值的类型,利于外部库进行函数调用(特别在外部库是静态类型时,比如数据库桥,这尤为重要),尽管动态语言的反射能力一般都非常强大,但如果其本身的语义不支持函数参数返回值类型,那么也是无能为力
静态类型检查,通过早期检查提高程序的健壮性,而如今设计模式的发展已经可以很大程度上弥补静态类型所导致的紧密耦合出现。而对于大型项目,特别是多个模块同时开发的项目,静态类型接口可以作为一种有效的契约来约束各个模块的行为,而静态类型检查有效地保证了这种约束

事实上可以认为前三点只能说是引入类型带来的额外好处,而真正重要的是则是后两点。从前面论述可以看出对于大型项目失去静态类型是一件非常不利的事情,这完全可以抵消使用高灵活性动态语言所带来的生产率的优势。在此我们深化本节开始的说明:作为一门更进一步的动静融合程序设计语言最重要的特性就是兼具动态类型的灵活性和静态类型的可靠性。

这两点看似矛盾,实际上如果我们将这两点放到不同的粒度上去考虑,在一个粗粒度的级别上用静态类型描述接口,而在内部是使用动态类型编写程序,可能在将来的Python3.0中我们可以这样来编写程序:
interface IBussinessLogicFacade:
    def insertOrder(order: Order, date: Date) -> boolean: #静态类型的接口
        “insert a Order return true if succeed”

class BussinessLogic(IBussinessLogicFacade)
def insertOrder(order, date): #动态类型的实现
    …
这样我们就既能有效的利用动态语言来提高生产率又能利用静态类型来巩固程序。

但是如果我们详细阅读Guido年初的文章,我们会发现Guido最后的定稿中的可选静态类型的语言特性只是他开始提出的全部想法中很小的一个子集,大量有趣的特性没有出现在Python3.0 Plans中,这包括泛型、重载等等,而且Guido还提到Python可能不会在language core级别添加静态类型检查等等功能――静态类型可能更多的是为外围的库(比如Pychecker,一个Python程序的检查工具)提供支持,这里的静态类型可能更多地沦为一种元数据的描述手段。这里我们可以体会到Guido的小心翼翼,毕竟对于动态类型为核心的语言在其上包装一层静态类型的壳,如果一不小心就会丢失掉动态类型的好处而还要承担大量反射带来的性能损失。而且一个兼具动静融合的语言可能同时需要强大的编译器和解释器,Python社区作为一个缺少大厂商支持的社区,如果语言设计过于复杂就可能超出其能力实现的范围就可能沦为下一个Perl 6。

C#,超级编译器

微软之于JCP,与其说是专政之于民主,倒不如说是私企之于国企更为恰当。对比微软来说,JCP做对一件事几乎和做错一件事一样难,当然大部分时间他什么也不做。反之微软开发技术每次的更迭所带来的变化总是令人乍舌(Dos到Win32,Windows DNA到.NET),而这也是微软最遭开发人员诟病的地方之一。

类似的,对比属于JCP的Java和属于MS的C#(实际上也包括VB.net,简洁起见以下略去),我们同样发现C#在语言核心变更上的激进程度令人惊讶。实际上如果我们注意Java以及更早的C++就会发现这些语言都是在努力保持着语言核心的稳定和类库的激进。但是不应忘记的是与它们不同C#事实上的一种厂商专有的语言(虽然被标准化),所以从微软的角度来说变更语言核心和变更类库是可以完全从技术上考虑,这使得C#较之商业语言更像一种实验室语言――并不排除这是MS打击Java的一种战略。唯一令人担心的是语言核心如此剧烈的变更会打击开源社区的积极性(比如Mono)。

回到正题,我认为微软很久之前就注意到了动态语言的重要性。实际上远在Win32的时代,VC+VB方案流行的时候,VB就可以称为一种基于对象的动态脚本语言。而微软从COM开始就致力于不同程序设计语言间的无缝集成,到了.NET的时代从设计之初不同语言的集成就是其重要的目标,所以我们也就看到了大量的将其他程序设计语言向.NET平台迁移的项目(Perl.net, IronPython, etc)。但也许现在MS已不再热衷于动态语言与静态语言在虚拟机一级的集成了,因为从不久前微软发布了一个C#3.0的预览版本来看(实际上从C#2.0就已经开始)MS似乎想在语言层面让C#尽可能地吸收动态特性,塑造出一种可能是兼备动态类型的灵活和静态类型的可靠性的语言,而这正是我们认为的未来语言的趋势。以下简单浏览一下C#3.0的特性,并讨论一下C#和Python在动静融合方面思维方式的不同。

C#3.0最大的一个特点就是赋予了编译器强大的类型推导能力,C#3.0的大部分新特性都需要类型推导能力。比如隐式类型局部变量(implicitly typed local variable)允许我们编写这样的代码:
var i = 5;
var s = “Hello”;
var d = 1.0;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary();
这里编译器会帮助我们根据初始化语句推导出变量合适的类型,而匿名类型特性以隐式类型局部变量为基础可以产生更惊人的效果:
var p1 = new { Name = “Lawnmower”, Price = 495.00 };
var p2 = new { Name = “Shovel”, Price = 26.95 };
p2.Price = p1.Price
p1 = p2;
注意p1和p2是可以相互赋值的。可以看到这段代码很像动态语言的代码。另外C#2.0引入匿名方法,C#3.0更是引入lambda表达式(其中隐性局部变量仍然起了重要的作用)这种函数式编程的特性。这些都反映出C#在吸收动态特性方面的努力。

另外值得我们注意的是C#进行动静融合时的思维方式。为了对比我们首先看Python。实际上一般来讲我们认为静态方面的语言特性由编译器来完成,反之动态方面的语言特性由解释器(虚拟机)来完成。Python本身作为一种动态语言,它在试图吸收静态特性时首先想加强的是它的编译器。直观地想,我们会认为C#既然拥有CLR这样强大的解释器(已经在其上实现了几个动态语言,证明了其能力)那么当它吸收动态特性时理应应用这一点――如果这样做C#3.0和Python3.0可能就没什么不同了。

但事实不是这样,这里不得不承认C#3.0在这方面是另辟蹊径,C#把这些动态特性大部分推给编译器――类比C++的静态多态(泛型),利用编译器的推理能力,使程序开始运行时仍然像个静态类型语言,但在编写时可以忽略大量类型的桎梏而很像动态语言。这里最大的得利就是效率――Python、Ruby总是被人们批评为“世界上最慢的语言”,因为今天硬件的表现还不足以让我们忽略反射的损失(虽然也许10年之后谈反射的性能损失就像现在谈论虚拟机的性能损失一样)。

当然这样的实际上是“伪动态类型”的程序设计语言的灵活性到底如何还有待大范围应用后考验,但微软在这里已经走出了第一步,而C#也符合了我们开始说的实验性语言的地位。

2005-12-8

 

Python AOP概览

Lightweight Python AOP
http://www.cs.tut.fi/~ask/aspects/aspects.html
足够简单,因此也很像个玩具或者随意的练习。
喜欢这个方案中把函数作为第一类对象的做法,wrap_around函数会动态complie一段代码来创建新函数来替代原有函数,并在新函数中调用方面(aspect)的代码和原函数。
缺点是当需要多个方面时它采用嵌套的方法来进行织入(weave),也就是说如果想在织入后从织入链中去掉一个方面会非常困难。而且把原函数改名保存的做法虽然直接看起来却不够优雅――如果类中恰好有和修改后的名称同名的函数怎么办?而且把改名后的函数(也包括其他一些需要的属性,比如方面调用时的堆栈)保存在函数所属类中,也就是说虽然在这个方案中把函数作为第一类对象来看待,但并不能把一般函数作为连接点(join point)。而对实例(instance)的织入会影响该类的所有实例。

Pythius.aop
http://cvs.sourceforge.net/viewcvs.py/pythius/pythius/pythius/aop.py?rev=1.36&content-type=text/vnd.viewcvs-markup
用metaclass实现的Aop,如果对应到Java上Aop的实现可以说是非常相似了――或者说很像是实现了Python版的动态代理。pythius.aop.Metaclass(这个名字起的可够糟糕……)作为元类(metaclass)其实例化的类会修改__getattr__、__setattr__的实现在其中拦截全部调用――包括函数、变量等等。它提供的例子是这样的:
>>> class Square:
        __metaclass__ = pythius.aop.Metaclass    # This is constant!

        # This changes!  Note that we are referring to an *instance*
        # of an Aspect, not the Logger class itself.
        _aspect = my_logger
        …
这里织入的声明包含在类声明中,于是类和方面间是有很强的耦合,实际上这是完全没有必要的。完全可以把这个织入的声明抽象出来
>>> SquareWithLogger = pythius.aop.Metaclass(&aposSquareWithLogger&apos, (Square,), {&apos_aspect&apos:my_logger})
这会产生一个很像Decorator的子类,值得注意的是如果用动态代理来实现Aop那么动态代理其实不如叫作动态装饰器。这个实现方法支持方面extend来合并多个方面,不必非要进行嵌套织入,理论上也可以修改织入链――但没有提供对应的方法。
问题在于不支持对一般函数的织入。不支持实例的织入。Metaclass对类的改动太大,有些直觉上是不必要(比如已经通过修改__getattr__实现拦截为何还要从类中删去连接点的声明),可能导致一些依靠反射程序无法正常运行。另外类中具体的函数连接点实际上是被定义在方面中的,这个耦合很糟糕。

Aspects
http://www.logilab.org/projects/aspects/
定义了一个抽象方面AbstractAspect在其中保存原函数和方面――用户可以扩展这个方面,然后定义一个创建函数的函数在被创建的函数中依次调用方面和原函数。这个和Lightweight Python AOP的思路很像,但是因为用抽象方面把信息抽象一致地保存所以就不需要动态compile出新函数――相比之下Lightweight Python AOP通过一个递增的数字拼接处字符串来记录原函数和方面的做法就太过粗糙了。支持实例的织入。而且这个方法的思路本质上是可以支持一般函数的织入的,可惜在构造AbstractAspect时默认传入的参数是成员函数――但是可以很类似实现出支持一般函数的织入的抽象方面和调用函数。
另外,Weaver作为一个Singleton实现其不仅提供织入的功能同时还保存了被织入函数和方面的信息(比如防止一个方面被织入同一函数两次――不过我觉得这个假设毫无道理),这个Weaver很像是个容器。好处是虽然也采用了嵌套织入的方式但根据Weaver中的信息可以实现方面的拆卸――但我认为应该有依赖性更小的方式。
这个方案可以说十分注意不影响连接点,比如新函数替换连接点函数时保持__doc__仍然可用,值得借鉴。

PyContainer
http://pycontainer.sourceforge.net/
实现了Interceptor,还算不上Aop。另外实现的方式很奇特(如果不是奇怪),利用container的优势返回包装过的实例,这个实例重写了__getattr__方法当调用存在拦截器的函数时就会返回这个实例自身……因为这个实例同时实现了__call__方法其中依次调用拦截器和原函数。整个项目还是一个很粗糙的版本而且已经一年多没更新过了。

AOPTutorial–Doing AOP With TransWarp (Under Construction)
http://www.zope.org/Members/pje/Wikis/TransWarp/AOPTutorial
不知道这个该不该算成一个Aop的方案,因为看这篇文档感觉主要是在讲Mix-in。其中的Aspect像一个可以动态添加的基类。有趣的概念是如果Aspect包括一个类体系那么它每次混入的结果都会产生一个新的类体系。具体看文档吧,一两句话说不清楚。另外没看到代码不太清楚是怎么实现的,因为实在懒得下一个Zope了。

理想中的Python AOP
   方面和类不应该存在任何耦合
   支持函数、属性作为连接点
   支持把各种函数都作为连接点,包括function, unbound method,  instancemethod, class method, static method
   支持before, after, around, throw
   织入后除了连接点之外不会对程序的其他部分产生影响,比如dir()、__doc__应该还能正常工作
   支持运行时刻连接点上方面的添加和卸载
   不依赖容器

Asp.net vs JServlepts+

"JServlepts+"是"Jsp + Servlet"的一个拆拼,学习"Tom Marvolo Riddle"=>"Lord of Voldemort",嘻嘻

servlet像个开瓶器,不过切水果极不好用。Sun发现这一点之后就搞了个水果刀jsp专门用来切水果。本以为这样就好了,可惜不久很多人发现水果刀也能凑活用来开汽水瓶,于是他们嫌麻烦就用水果刀开瓶,结果总是弄伤手。

Sun一看这样不行呀就弄出一堆指南“开汽水瓶用开瓶器”、“切水果用水果刀”、“吃饭的时候饮料上来就拿出开瓶器,水果上来就拿出水果刀”,这些写了许多本书(core j2ee pattern,etc)

有些人说这也太麻烦了,就把水果刀、开瓶器组装起来弄成一把“瑞士军刀”(MVC),这样就不会出现手头上没有合适工具的情况,但是这也不能避免误用,而且携带起来也太笨拙了。

M$说你们这也太没有发明创造性了,于是他把开瓶器的两端磨出刃来,说这是个叫WebForm的颠覆性万能工具――其实在M$提出之前很多人都有这个创意了,不过M$从来也没有什么创意,只是在把一个想法实现成一件精美的产品方面,M$无出其右。

好了Asp.net这个东西轻便了很多,而且对于Sun的那一堆指南,asp.net几乎在大多数情况下都能出于本能地对应(事件处理函数对应应用控制器,behind code对应视图助手等等)

虽然个人觉得能在事件处理函数中同时调用Model操作并且控制View控件(只要你愿意那么这些可以以一种OO的方式来进行)绝对是一种进步,但这不能否认asp.net带来的好处使它极为复杂――相对servlet+jsp。比如如果你不搞清楚asp.net的页面生命周期,那么你几乎总会犯错;而对于servlet+jsp来说可能了解http协议的一次响应流程就足够了。所以这可能反而引发更多的误用,特别当M$似乎从不喜欢出一些有条理的指南(相对于一本对知识进行系统描述的书来说MSDN就是一锅粥)或者人们还没总结出有条理指南的时候,这一切尤为明显。

M$,也许你认为身为一个开发人员理应有能力弄清这一切,可反过来说你不是一直在着力让隔离的玛格丽特阿姨来抢大家的饭碗吗?这真是一件奇怪之极的事情。

ADO.NET乱弹

随手写的数据库课作业,可能有错,看看就好

—————————————————————-

ADO.NET是M$最新的数据访问技术。在windows 2000的技术体系中数据访问技术被称为ADO+――类似于COM+、Form+、ASP+,但是2000发布时只携带了COM+的实现。而随着M$的.NET战略,ADO+最终被用基于.NET的语言所实现,表现为.NET Framework中的一组类库,并命名为ADO.NET。
因为M$善于混淆完全不同技术的特长,ADO.NET被命名了一个类似ADO的名字,然而我们注意到实际上ADO.NET与ADO几乎没有任何关系,这是一个不同的新模型。ADO.NET遵循更通用的原则,不那么专门面向数据库。ADO.NET集合了所有允许数据处理的类。这些类表示具有典型数据库功能(如索引、排序和视图)的数据容器对象。从总体设计上来看,ADO.NET不像ADO模型那样以数据库为中心,这是 ADO.NET 的一大特点。
ADO.NET包含两种数据访问方式――在线的以及离线的。在这里是存在争议的,因为有人认为这两种方式是同样重要的。然而个人观点是,在线模型更多的是为实现离线模型而存在的。当然不否认在线模型在访问小量数据以及悲观并发时的作用(另外当ADO开发人员尝试过渡到ADO.NET上时在线模型可以起到让他们误以为两种技术相似的混淆作用),但从.NET的技术体系上讲离线模型才是被推荐的。以下只讨论离线模型。
DataSet是ADO.NET(1.0版本中)离线模型中存储数据的核心类。DataSet的重要之处在于它不仅包含DB中的data,还包含DB的meta-data。DataAdapter连接数据库填充DataSet时,它同时将DB中的结构信息填充入DataSet,这包括列数据的类型、约束等等。这样就等于在内存中建立了一个数据库的副本――当然只包含小量的数据。而操作这个内存副本就像操作数据库一样――比如可以使用Sql来对其进行检索。
另外DataSet的重要特性是它在其中数据发生修改后,它会保存数据为修改时的副本。这样DataSet天生就是乐观并发的。实际上在DataAdapter连接DB更新DataSet中的数据时,它默认使用乐观并发的策略。
除了在运行时刻填充DB的meta-data外,M$提供一种称为强类型数据集的方式来访问。通过VS.NET中提供的一系列Wizard来在静态时创建一个类型安全的数据库结构映像――强类型数据集。这样我们就可以避免了大量诸如:
r[‘count’] =  (int)r[‘count’] ?C 1;
的操作,而以一种直观的方式来使用DataSet(在.Net语言的Property特性的帮助下):
r.Count = r.Count ?C 1;
然而使用向导的一贯问题在这里依然存在。任何对向导生成代码的修改,在每次重新生成后都不复存在。而且这种代码生成方式给程序员带来测试困难以及不可控感一直广遭诟病。当然如果我们考虑到M$的主要精力总是放在降低程序员的门槛以及不断加强所谓的“快速开发”上,那么以上的行为全都不难理解。
从上我们可以看到在一定程度上ADO.NET超出了一般的数据访问技术。DataSet(特别是强类型DataSet)在DataGrid的辅助下就足以构建一个解决方案。但正如Martin Fowler在PEAA中谈到的,人们经常批评M$的解决方案中没有对象。DataSet实际上是一种被Martin Fowler称为表模块(以及表入口)的业务层+数据源模式,然而在OO被广泛证明价值的今天,想用DataSet+DataGrid来完全代替OO+O/RMapping,可以认为是可笑而愚蠢的――当然我们还是要注意M$的精力主要放在“快速开发”上。
值得注意的是一个多月之前,在Channel9上Ander向我们展示了一个被称为DLINQ的疑似O/RMapping方案(在TSS上有争论)。在ObjectSpace失去音信的时候,这也许是值得每一个.NET开发人员期待的。