论面向组合子程序设计方法 之 创世纪

发现老庄的连载方法很好.又能吸引眼球又能好整以暇.于是从善如流. 


这几天在完善我的neptune系统和jaskell语言。顺手发现了一个logging的需求。如获至宝阿。 

为什么呢?不是因为这个需求多么难,或者我的解决方法多么巧妙,而是因为,这个例子足够简单,直观,要说明它,背景知识几乎不大需要,三两句话大家就明白需要达到什么效果。这种例子可不是随便就想得到的。 

而同时,它又对实现提出了一定程度的灵活性要求,正好方便我展示我叫做“面向组合子”的程序设计方法。 


说到这里,不禁又有点沮丧,我也挺想象别人那样,先高举高打,玄之又玄,弄些哲学思辨,什么佛法呀,道德经阿,西游记亚,以及各位西方先哲的亟语,甚至量子力学的悖论。这样才能吸引眼球,增加人气呀。 
可是,等而下之的工匠气作祟,说着说着就要拐到具体例子上去了。不争气呀。 


算了,不想那么多了。论道不是俺这种俗人所擅长的,还是鼓捣“器”吧。 


大致的背景是这样: 
我的neptune是一个build system,在build的过程中会产生很多log信息。这些信息分为不同的重要级别。 

说到这里,肯定有人已经按奈不住:用Log4j! 

先不要急,我们这里不是要告诉你怎么处理你得程序中的logging需求,而是要通过这样一个容易理解的例子来说明以下“面向组合子”的编程方法。所以,这里让我们先假设我们不知道什么log4j。什么是log4j呀? 

当然,大致的思路总归差不多的。因为我的neptune系统只需要一个logging的工具,而不关心这个logging工具是什么,这就是一个perfect的依赖注射的场合。 

先定义接口Logger,然后从构造函数传递近来一个Logger实例,接着就直接调用Logger就是了。 

Java代码  
  1. public interface Logger{  
  2.   void print(int level, String msg);;  
  3.   void println(int level, String msg);;  
  4.   void logException(Throwable e);;  
  5. }  


print用来输出信息,但是不折行,println可以折行。 
logException用来直接纪录异常。这样,对异常是直接printStackTrace,还是println(e.getMessage())就是由具体的Logger实现来决定,我的neptune只需要把遇到的异常报告给Logger就是了。 


好。接下来我吭哧吭哧地把neptune完成了,剩下的就是从哪里找一个Logger实现了。 

最简单的Logger实现自然就是直接往屏幕上打印了: 

Java代码  
  1. class SimpleLogger implements Logger{  
  2.   public void print(int lvl, String msg);{  
  3.     System.out.print(msg);;  
  4.   }  
  5.   public void println(int lvl, String msg);{  
  6.     System.out.println(msg);;  
  7.   }  
  8.   public void printException(Throwable e);{  
  9.     e.printStackTrace();;  
  10.   }  
  11. }  


直接把这个SimpleLogger注射进我的neptune,整个系统就可以工作了。 
no big deal,对么? 


好了,下面我们开始真正实现完整的logging系统了。经过分析,我们大致有以下的需要: 

[list]1。logger可以把信息打印到log文件中。 

2。不同的重要程度的信息也许可以打印到不同的文件中?象websphere,有error.log, info.log等。 
如果这样,那么什么重要程度的信息进入error.log,什么进入warning.log,什么进入info.log也需要决定。 

3。也许可以象ant一样把所有的信息都打印到一个文件中。 

4。每条logging信息是否要同时打印当前的系统时间?也是一个需要抉择的问题。 

5。不仅仅是log文件,我们还希望能够在标准错误输出上直接看见错误,普通的信息可以打印到log文件中,对错误信息,我们希望log文件和标准输出上都有。 

6。标准输出上的东西只要通知我们出错了就行,大概不需要详细的stack trace,所以exception stack trace可以打印到文件中,而屏幕上有个简短的exception的message就够了。 

7。warning似乎也应该输出到屏幕上。 

8。不管文件里面是否要打印当前系统时间,屏幕上应该可以选择不要打印时间。 

9。客户应该可以通过命令行来决定log文件的名字。 

10。客户可以通过命令行来决定log的细节程度,比如,我们只关心info一样级别的信息,至于debug, verbose的信息,对不起,不要打印。 

11。neptune生成的是一些Command对象,这些对象运行的时候如果出现exception,这些exception会带有execution trace,这个execution trace可以告诉我们每个调用栈上的Command对象在原始的neptune文件中的位置(行号)。 
这种exception叫做NeptuneException,它有一个printExecutionTrace(PrintWriter)的方法来打印execution trace。 
所以,对应NeptuneException,我们就不仅仅是printStackTrace()了,而是要在printStackTrace()之前调用printExecutionTrace()。 


12。neptune使用的是jaskell语言,如果jaskell脚本运行失败,一个EvaluationException会被抛出,这个类有一个printEvaluationTrace(PrintWriter)的方法来打印evaluation trace,这个trace用来告诉我们每个jaskell的表达式在脚本文件中的位置。 
所以,对应EvaluationException,我们要在printStackTrace()之前,调用printEvaluationTrace()。 

13。execution trace和evaluation trace应该被打印到屏幕上和log文件两个地方。 

14。因为printExecutionTrace()和printEvaluationTrace()本身已经打印了这个异常的getMessage(),所以,对这两种异常,我们就不再象对待其它种类的异常那样在屏幕上打印getMessage()了,以免重复。 

15。也许还有一些暂时没有想到的需求, 比如不是写入log文件,而是画个图之类的变态要求。[/list:u] 

大致上,我目前遇到的需求也就是这些了。 

好了,允许我卖个关子,下回分解的时候再说怎么用“面向组合子”和依赖注射的方法来解决这个问题吧。 

在本节结束之前,我稍微提一下“面向组合子”的来历。 


组合子,英文叫combinator,是函数式编程里面的重要思想。如果说OO是归纳法(分析归纳需求,然后根据需求分解问题,解决问题),那么“面向组合子”就是“演绎法”。通过定义最基本的原子操作,定义基本的组合规则,然后把这些原子以各种方法组合起来。我最近一段时间做的东西,jaskell不用说了,函数式语言。yan, neptune, jparsec全是用面向组合子的思想开发的。 

OO就像是猜谜,给你一个苹果,然后问你:这个苹果是怎么得到的呢?然后你分析一番,说:我认为这个苹果是由分子组成的,这些分子如此这般排列,然后分子又由原子组成,如此这般排列... 

而CO(面向组合子),就等于是说:这有H, C, O三种原子,强弱两种作用力,你来看看能做点什么出来吧,然后你就像搭积木一样,把这三种原子,两种作用力搭建出这大千世界,什么毛毛虫,狗熊,周星星,不小心,一下就做出了一个苹果。 

OO的关键是需求。 
所谓"refactor",不过也是强调需求,让你不要自作聪明地瞎假设需求而复杂化设计。时刻着眼于当前的需求。这样,一旦需求变更,所浪费的力气可以保证最小,而且,船小才好调头嘛。 
如果需求分析的不好,一切就歇菜了,虽然因为一些比如ioc之类的设计方法能让你不至于推到重来,但是需求仍然是重中之重。 


那些什么上下文没有,上来就说“怎么用OO来做一个人骑车呀?”,“是人.骑(车)呀?还是车.被骑(人)?”纯粹是没头没脑地瞎掰。 

而CO的关键则是组合子和组合规则的设计。这些组合方法必须非常精巧,尽量正交。组合子的设计既要简单(越简单才越容易被组合),还要完整。 
比如说,对整数这个组合子,我们有+-*等组合方法,这样只要有了0,1这两个组合子,我们就可以构造出整个整数世界。 
可是,精巧的组合子设计也不是那么容易的。需要有一点点数学的感觉和严密的逻辑思维基础。 


有人说,上帝是用OO设计的世界,可要我是上帝,我宁可用CO。 
设计几个简单的基本粒子,几个简单的相互作用力,然后让这些东西自己组合,随意发展,不是比事必躬亲,先想透彻了自己想让世界是什么样子,然后一张一张图纸地具体设计,一个一个人地造, 
“撒旦,你去干坏事,往死了整这帮贱人!” 
“天使,你去干好事,打个巴掌再给他们点甜枣吃。” 
“儿子,你下去混混,看丫敢不敢钉死你!” 
来的容易美妙? 

哈。终于形而上起来了,爽!

from:http://ajoo.iteye.com/blog/23303

你可能感兴趣的:(设计模式)