阅读更多
ajoo牛 代码观感
刚开始接触JParsec的时候,我就对照着ajoo的calculator, mssql的等比较复杂深入的例子,向下挖掘,并随之构建一套类体系。遇到不懂的地方,就向ajoo询问。
ajoo给我的建议是从简单的步骤开始做起,这样更有信心。
我的回答是,简单的步骤并不能免除我今后应对复杂的地方。现在弄不懂的,后面还是不懂。我习惯于一开始尽量把所有障碍和难点思考清楚,提前拔除。
ajoo说,呵呵,随便你。只要你觉得能够有信心。
作大牛的跟随者,作ajoo牛的跟随者尤其难。
ajoo太敏捷了,步子迈得太快了。
讲解monad parser不久之后,JFun就问世了。JParsec,Jaskell就问世了。
几天前看他在询问和讨论Class Loader,几天后IoC container就出来了。.net版本的JParsec也是如此。
就说这个JParsec,前几天抱怨java generic is a mistake。过几天,JParsec就换上了Generic Version。前几天抱怨Groovy,过几天,Groovy的JParsec配套措施和例子就出来了。
ajoo的编程语言功底很深,一门新的working language,拿来之后,不是首先理解里面的语法特性,而是寻找其中的语法特性。因为他在此之前早已了解各种语言语法的超集,不会对什么语法特性感到惊奇,看到一门语言,首先是抱怨这个都没有,那个都没有,而不是,竟然有这个,竟然有那个。
正是因为如此,ajoo对各门新兴语言的掌握得心应手。
另一个例子是,ReadOnly。ReadOnly的特点不是掌握语言快,而是掌握应用领域框架非常快。Spring, Hibernate, WebWork,各种东西一出来,就玩的很熟。我想,这只能用头脑和经验来解释了。
ajoo牛的这种大步子,令追随者很难跟上。我遇到的主要障碍倒不是语法,咱也是当年跟着T1从SICP开始混的。主要难度在于ajoo牛的设计思路。
JParsec是一个Haskell Parser( Parsec) 的移植。我想,要想理解JParsec,从Haskell Parsec开始可能更深入一些。结果证明,这是一个错误想法。Haskell Type System的语法,尤其是Monad部分,很难理解。还是要从ajoo牛的Java代码看起,更容易一些。
不管怎么说。这个难点,不是那么容易克服的。我只好先把能够做的事情,向前推进。就是给已经搭建的简单语法结构,加上测试。果然如我所料,select from where group by having order by 这些简单语法的解析,一两个小时之内,轻松通过,毫无悬念。
正印证了我前面的说法,简单步骤,对我解决后面的复杂难点毫无帮助。我还是卡在这里了。前面看到的解决不了的难题,如果不解决,到后面遇到了,还是要解决。
那么就看JParsec的代码吧。
和ajoo讨论JRC Parser的设计过程中,发现ajoo和我的很多类似的习惯。比如,对cross/recursive/cycle reference很敏感。会尽量避免这些问题。
ajoo的代码有个特点,清晰,优雅,强悍,拙朴。怎么说呢,比如,一个use case ( such as the mssql parser),有40个类,那么其中39个类,每个类都不超过10行代码,但是剩下一个类,用来组装各个组件的那个类,可能有上千行,几千行代码。
对于ajoo来说,根本不在乎那些教条,什么面向接口编程。没有必要的情况,根本就不抽出接口。JParsec的参数和返回值都是Parser,这是一个千行大类,而不是一个接口。ajoo在乎的是,用户固定使用的亘古不变的高阶逻辑(组合子之间的关系)。换句话说,就是那些Function Name。这里面,Functional Programming的痕迹非常明显。
这种藐视一切法则,却又如此清晰优雅,又如此古拙强悍的设计,奇妙的显示在一个人的代码里面,真是惊人,惊为天人。比如,Parsers和Parser两个千行大类(Parsers有接近5000行,当然,至少一半以上,是详细的javadoc),明目张胆地进行cross reference。
看到这块地方,我心里往往有些小小得意。嘿嘿,我解耦的能力是相当强的,多年以来就是干这个的。构造的这个JRC Parser结构,虽然组合关系也是非常复杂,相互引用,但是我大量使用面向接口,IoC这类平庸的手段,把Parser组合类拆成了很多个类,每个类不超过80行。不可否认,这个设计是毫不出彩的,但是,在分解代码职责的同时,保证了代码里面没有一处cross reference。
下面说JParsec本身。初步印象是,一个大一统Context参数包含所有了所有需要交互的信息。这就是一个Continuation Passing Style。由于OO的数据构造优势,使得这类State传递的实现非常简单。以前在Haskell需要nested closure结构才能做到,在OO里面轻松用顺序结构就可以做到。
这样看来,从JParsec去理解Haskell Monad Parser对于OOer来说,是一个正确的方向。
关于单元测试。
firebody和我说,ajoo的强制单元测试做的很好。
Yes.在我看来,ajoo的 Test = Sample = Demo。每个test都很复杂,很完整,代表了一个完整的use case。
我喜欢看这样的测试。对我来说,这样更容易理解。我反而不容易理解那种一段一段的测试某个method某一部分功能的散落的测试。当然,这也很可能是我阅读代码能力不强。