浏览 70238 次
该帖已经被评为精华帖
作者 正文
时间:2006-09-19
引用
我们现在还应该考虑的一个问题是,为什么动态性在现在忽然变得重要起来,而到底动态性带来了什么。

其实,这个问题一点也非常的简单.
从原始的OO,到后来的Design Pattern,AOP,IOC之类的玩意,捣鼓了十几年唯一要解决的事情就是其实就是一个Message Dispatch的问题.Message Dispatch的本身就很简单,它有一条消息比如说一封mail,然后给出一个消息传递的目的地址,比如说一个mail地址.然后有一个后台运作自动的分派器,通过识别目的地址的格式将消息发送到目的地.
最原始的OO的消息分派,是派生体系之间自顶向下的分派.消息就是各种抽象函数调用,而目的地址就是各种类型.这个种消息分派的方式由编译器或者解释器来承担那台自动分派器的角色.在特定的情景下的确起了非常大的作用,比如说GUI,在GUI的世界里本身就是一个消息满天乱窜的世界,这给了传统OO以最大的活动空间.

但是贪心的程序员们还不满足于消息分派的路径被继承体系给框死.他们自始至终的一个贪念就是,让低级的程序员发送Message的时候指那儿打哪儿,至于Message具体怎么dispatch不需要他们关心.这就是所谓的封装和抽象.
于是他们发明了各种各样的Design Pattern,来实现各个继承体系之间的消息分派.比如最典型的AbstractFactory,我有几组相同行为的Factory,我想把一个消息Createxxx发到一个单一的FactoryofFactory的接口,并且通过XML文件比如说像这样
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="factoryName" value="AmericanFactory"></add>
</appSettings>
</configuration>
或者其他方式告诉这个消息的目的地址.AbstractFactory收到这个目的地址,就能准确的把Createxxx发送给目的地.而后贪心的程序员还不满足,为了在继承体系横向之间通信,搞起了AOP,为了让目的地址分派更加方便搞起了IOC.
所有的这一切,程序员无非做的是两件事情,第一定义一个目的地址的格式,第二根据目的地址的格式写一个分派器.但是很不幸的事情是,一来,这一切工作再也没有编译器和解释器的帮忙,完全要靠程序员们手工书写代码.二来,当目的地址的格式变得越来越复杂的时候,它本身变成了一种描述dispatch过程的代码,你必须用一个有完整语义和文法的语言将其描述出来,这就是一开始采用的XML的原因.
如此复杂的工作,对于程序员来说工作其实从指那儿打哪儿 变成了打哪儿指那儿.于是便有了软件蓝领和架构师之类的等级制度,俺们架构师用打哪儿指那儿的技术给你们软件蓝领们提供指那儿打哪儿的架构.设计是俺们的事情,你们就在下面给我老老实实的编码.

于是乎,有些聪明的程序员说,你们这些架构师也属于多余的寄生阶级,你架构师捣鼓了半天那么多劳什子的东西不过就是在干消息分派的活吗?我编译器解释器里面的那些AST tree,Stack Machie那么强大的消息分派机制只是让你用来分派类型实在是浪费啊.所以干脆我把语言级别的分派器开放给你.由我来写一个功能强大的消息分派器,但是呢这个消息分派器不像OO语言那样是固定死的,而是可订制的.我通过公布一些Dispatch描述规则,就能让程序员描述出他们需要Dispatch的路径,而我的解释器或者编译器就负责能把消息根据这些Distapch路径分派到你要的目的地.
于是乎,一夜之间所有的原本Design Pattern,XML,AOP要干的活编译器解释器全包了,那些既无聊又难以维护的Dispatch代码都是编译器解释器的工作,而程序员只需要知道解释器编译器所支持的Dispatch描述规则,来编辑消息目的地址或者消息分派路径,这就是所谓的DSL.它把架构师们原先的两个主要任务简化为一个而且是把原先最有技术含量最复杂也最能唬人的任务简化掉了.

动态语言的确给程序员们带来了指那儿打哪儿的快感,就是我说的肯德基和中餐馆的区别.有些人,比如说熊杰已经意识到高压锅+定时器同时炸几十个鸡腿肯定比架个油锅炸来的快同时也来的没技术含量,于是他们转向了耍嘴皮子搞咨询服务的行当.另外一些人当然也看到了油锅炸鸡腿肯定要淘汰,于是乎到处宣传他们这是百年老店的工艺不是高压锅和定时器能炸出来的.问题是,大多数的客户要吃的是鸡腿,而不是看你炸鸡腿的手艺,否则大家画鸡腿充饥好了.
再我看来,Ruby的确很红火,但是跟不跟近的问题到数次要.一个原先名扬四海的大厨干的活一个失业青年2天培训就能干,活是肯定比以前轻松了但是这条路还有以前那么宽广吗?
   
0 请登录后投票
时间:2006-09-19
楼上早。
同意T1。动态性就是带来了松耦合,把静态语言的dispatch by type强契约,变成了dispatch by name,甚至dispatch by pattern等弱契约。
到了最后,dispatch 干脆就弱化为 DSL,Message Protocal。到了Message Protocal,这就是SOA。动态性最终带来的就是DSL, SOA。

关于静态类型的更为臭名昭著的例子是Visitor Pattern的Type Dispatch。有两个选择,
或者使用 instanceof/Class.isAssignableFrom/Type Cast,或者引入visitable (or visited) interface。

由于摆脱了类型契约,动态语言中就没有这个麻烦。

另,T1咋就盯上了吱吱熊,有事没事就拿咱小熊作靶子呢?
   
0 请登录后投票
时间:2006-09-19
buaawhl 写道

另,T1咋就盯上了吱吱熊,有事没事就拿咱小熊作靶子呢?

大马鹿!!
你没看到我是在赞他吗?
另外这也要怪他的发型太像鲤鱼春.
   
0 请登录后投票
时间:2006-09-19
T1前面对OO语言演化历史的生动总结令人叹服。不过后面的例子举的不算太合适。

ruby火了以后,我觉得对程序员的要求实际上更高了。过去做Java开发,大规模工业开发下的每个个体都是零件,不要求你面面俱到,懂自己做的东西就行了。所以造就了一批Java基础(甚至编程基础)异常差劲的程序员,做的东西不知其所以然。

用ruby开发,一个程序员就不是写一个小小的模块代码了,而是一个系统就要你自己完整的实现出来,你啥都得懂才行。ruby on rails终究还是易学难精的。

如果ruby on rails真的成为主流了,那我的预测就是大部分水货程序员将被淘汰,剩下的精英程序员操起ruby on rails利器,1个顶10个,一个项目一个程序员就够了。(JavaEye2.0也是这样的)
   
0 请登录后投票
时间:2006-09-19
Ruby我没看过,不太清楚.但是我相信现在的门槛也只是暂时的.
还是拿KFC说事情,我读高二那会.我老爸就拿了一份报纸说,你看现在的大学生都去KFC打工,你暑假里也去干干?那个时候能去KFC打工真的就是像现在的外企Intern,但是我大学毕业的时候,KFC估计已经沦为失业青年的就业对象.倒不是说KFC档次差了,而是KFC之外的选择多了.
对于Ruby来说,It's not the end. It's not the beginning of the end. It's the end of the beginning.
编程的简化总是不可避免的事情,当初C->C++,C++->Java,都声称自己既是简化也是有门槛的,但是所有的门槛都经不起时间的踩踏.我上面的意思是,如果你想在自己的身后设立门槛保持竞争性,我觉得对于普通程序员来说两条路可以走1.寻找更新的门槛跨过去比如说你或者potain2.寻找完全不同方向的门槛跨过比如说Gigix.
对于后者我不了解,gigix来说更合适.对于前者来说,我觉得最好办法就是自己制造门槛.昨天和buaawhl再聊一些
Erlang的技术问题,我提出很多方案他都说因为没有现成的产品,所以想用其他现成方法绕过去.我看到的很多程序员都喜欢吃这样的现成饭,而不是喜欢用自己的双手劈开一条新的道路.再这方面Ajoo是我们应该学习的大牛,什么东西不爽就自己造一个,属于理论实际两手都硬的人。而像我就是属于做做科学普及工作,实际问题则懒于动脑子了.

PS,这个论坛貌似有一个问题,每个帖子有的时候会在一拦内显示不下,但是又没有滚动条,只能选择文字往下拖动够累的.
   
0 请登录后投票
时间:2006-09-19
buaawhl 写道
楼上早。
同意T1。动态性就是带来了松耦合,把静态语言的dispatch by type强契约,变成了dispatch by name,甚至dispatch by pattern等弱契约。
到了最后,dispatch 干脆就弱化为 DSL,Message Protocal。到了Message Protocal,这就是SOA。动态性最终带来的就是DSL, SOA。

关于静态类型的更为臭名昭著的例子是Visitor Pattern的Type Dispatch。有两个选择,
或者使用 instanceof/Class.isAssignableFrom/Type Cast,或者引入visitable (or visited) interface。

由于摆脱了类型契约,动态语言中就没有这个麻烦。

另,T1咋就盯上了吱吱熊,有事没事就拿咱小熊作靶子呢?


动态性和耦合性没有关系吧。写不写类型,类/对象之间的依赖性仍然存在,并不是说不写类型我就不依赖了。传统的耦合度是按照扇入扇出来计算的,明确类型的,在我看来这个系数反而小。个人感觉动态语言迟早得引入某种意义的契约机制,而一旦引入,这个契约机制在实际效果上就是变异的类型机制
一个明确的消息路由/转译和分发机制确实是转换耦合的好办法,只是效率太低.
不过,也不要提到耦合就不爽。其实耦合是比内聚更加重要更加关键的特性,一个没有一点耦合度的模块,一点意义没有。应该说耦合是软件开发的本质属性.耦合度太高才是一件坏事
   
0 请登录后投票
时间:2006-09-19
levinfox 写道

动态性和耦合性没有关系吧。写不写类型,类/对象之间的依赖性仍然存在,并不是说不写类型我就不依赖了。传统的耦合度是按照扇入扇出来计算的,明确类型的,在我看来这个系数反而小。个人感觉动态语言迟早得引入某种意义的契约机制,而一旦引入,这个契约机制在实际效果上就是变异的类型机制
一个明确的消息路由/转译和分发机制确实是转换耦合的好办法,只是效率太低.
不过,也不要提到耦合就不爽。其实耦合是比内聚更加重要更加关键的特性,一个没有一点耦合度的模块,一点意义没有。应该说耦合是软件开发的本质属性.耦合度太高才是一件坏事


谈一下耦合度这个名词。
我认为,耦合度是编译解释期,而不是组合运行期,代码之间相互依赖的情况。
如果是因为耦合度这个词语解释的原因引起的疑惑,那么这个问题实际不存在。

静态类型, caller 和 called 之间必然存在一个依赖关系。必然有一方离了另一方就无法单独编译。
这个依赖关系通常是一个抽象类型,比如interface。这个interface或者属于caller,或者属于called一方。如果interface属于一方,那么另一方就依赖于拥有interface的一方。或者这个interface单独抽出来成第三方,那么caller 和 called 都依赖于这第三方。总之,这个编译依赖关系是少不了的。具体代码就不给出了。估计大家整天思考分层,cross reference,应该深有体会。

动态类型举个例子。

caller{
process(param) { param.doSomething();}
}

called{
doSomething(){}
}

这两段代码完全是独立的,可以分别单独解释编译,完全不需要另一方。
相互得知彼此的存在,是运行时候发生的。只有在运行的时候,把called instance传给caller的时候,caller才第一次知道有called的存在,才试图去理解called。
   
0 请登录后投票
时间:2006-09-19
这么多高手的论点看得我眼花!

对我而言,只想找到一种盈利点,能挣到钱,那管它ASP\PHP\J2EE. 还是别的什么?

现在JAVA有积累我就用JAVA. 就是JAVA已经死了,只要我用JAVA比别的快速和有效率,全世界都没有人用了,我还是会用JAVA. 只要能带来钱就可以了!

为什么大家都喜欢脱离现实,挣论这些东西呢? JAVA死不死,以后才知道,现在没死就行.以后死了就以后再说,以后再换别的什么开发新语言! 发什么愁呢?真是没事做?
   
0 请登录后投票
时间:2006-09-19
一个语言死不死,完全根据大企业的高层的战略目标,世界经济的走向,有关系的.GOOGLE一个公司就能带来技术大规模的变化.世界任何技术,任何行业都是市场,需求决定的.当一个辛辛苦苦的农民用了最先进的嫁接技术种出了大量的水果,市场没有需求,又有什么用,还不是烂在地上.一切根据客户需求,市场需求,来指导技术才是关键.
我认为IBM,GOOGLE是技术中坚力量.看他们以后的发展应用了.一个技术的好坏,是离不开大量公司的支持.要等JAVA死亡,就等IBM,SUN,BEA,ORACLE等大量公司转型了.
   
0 请登录后投票
时间:2006-09-19
buaawhl 写道

谈一下耦合度这个名词。
我认为,耦合度是编译解释期,而不是组合运行期,代码之间相互依赖的情况。
如果是因为耦合度这个词语解释的原因引起的疑惑,那么这个问题实际不存在。

也许分歧就在这里?
其实极端一下去想,如果不计较运行期耦合,那么,所有无类型声明的纯OO语言(不论是动态还是静态),都是貌似无耦合的(实际上即便如此,耦合性仍然存在,后面会谈到)

引用

静态类型, caller 和 called 之间必然存在一个依赖关系。必然有一方离了另一方就无法单独编译。
这个依赖关系通常是一个抽象类型,比如interface。这个interface或者属于caller,或者属于called一方。如果interface属于一方,那么另一方就依赖于拥有interface的一方。或者这个interface单独抽出来成第三方,那么caller 和 called 都依赖于这第三方。总之,这个编译依赖关系是少不了的。具体代码就不给出了。估计大家整天思考分层,cross reference,应该深有体会。

静态类型语言中,从依赖关系来说,静态的依赖也就反映了动态的依赖。
所以在那个语境下,大家都不会去谈动态耦合,因为两者实际上是一致的. 也因为这一点,静态语言的可理解性要比动态类型语言强得太多。对于动态类型语言,不可能从一个对象知道它是干吗的,读者只能通过代码中对这个对象的操作来知道能对它干些什么事情。改代码的话,少干一些可以,想多干一些就有足够的麻烦


引用

动态类型举个例子。

caller{
process(param) { param.doSomething();}
}

called{
doSomething(){}
}

这两段代码完全是独立的,可以分别单独解释编译,完全不需要另一方。


实际上动态语言的预编译和静态语言的编译阶段有非常大的不同,多数动态语言,所谓的编译都是语法上的,根本不校验某个变量存在不存在,某个对象是不是存在被调用的方法。而且因为变量/函数的动态可替换性,即便看起来存在静态依赖,实际上是不是如此也是不确定的。下面这段python代码:
    def dosth():
        print 'xxx'
    def caller2():
        dosth()


看起来caller2依赖于dosth这个函数实体,而运行时因为dosth这个变量是可替换的,所以实际上依赖的是dosth这个名字.而且,python的预编译阶段并不会去确认dosth这个变量是不是存在. 当然,在这个情况下,caller2实际上产生了一个严重的外部/公共耦合. 那么,对于
caller{
   process(param) { param.doSomething();}
}

caller对param的耦合度究竟怎么算?

引用

相互得知彼此的存在,是运行时候发生的。只有在运行的时候,把called instance传给caller的时候,caller才第一次知道有called的存在,才试图去理解called。

在这里,caller并不是去理解called,而是去理解param中是不是有doSomething这个方法。这个至少是标记耦合(因为param这个参数肯定是外面某个东西传入的),至于是不是更加严重的,我很难判断。

但是,对于带接口的语言来说,可以有很漂亮的解决办法(伪代码)
def interface dsth:
    doSomething()

def caller:
    def process(dsth param):
        param.doSomething()

def called(dsth):
    def doSomething():
        ....
    ........

这个时候,caller确切的依赖/耦合于dsth,在静态语言情形下即便在运行期和called一点关系没有(因为不能调用called所定义的dsth之外的方法); 而动态语言情况下,如果约束了被声明类型的对象的调用方式,那也是如此.
   
0 请登录后投票
论坛首页 软件开发和项目管理版

跳转论坛:
JavaEye推荐