论面向组合子程序设计方法 之 创世纪 OOlog4j编程Websphere脚本
程序员文章站
2022-07-14 14:46:33
...
发现老庄的连载方法很好.又能吸引眼球又能好整以暇.于是从善如流.
这几天在完善我的neptune系统和jaskell语言。顺手发现了一个logging的需求。如获至宝阿。
为什么呢?不是因为这个需求多么难,或者我的解决方法多么巧妙,而是因为,这个例子足够简单,直观,要说明它,背景知识几乎不大需要,三两句话大家就明白需要达到什么效果。这种例子可不是随便就想得到的。
而同时,它又对实现提出了一定程度的灵活性要求,正好方便我展示我叫做“面向组合子”的程序设计方法。
说到这里,不禁又有点沮丧,我也挺想象别人那样,先高举高打,玄之又玄,弄些哲学思辨,什么佛法呀,道德经阿,西游记亚,以及各位西方先哲的亟语,甚至量子力学的悖论。这样才能吸引眼球,增加人气呀。
可是,等而下之的工匠气作祟,说着说着就要拐到具体例子上去了。不争气呀。
算了,不想那么多了。论道不是俺这种俗人所擅长的,还是鼓捣“器”吧。
大致的背景是这样:
我的neptune是一个build system,在build的过程中会产生很多log信息。这些信息分为不同的重要级别。
说到这里,肯定有人已经按奈不住:用Log4j!
先不要急,我们这里不是要告诉你怎么处理你得程序中的logging需求,而是要通过这样一个容易理解的例子来说明以下“面向组合子”的编程方法。所以,这里让我们先假设我们不知道什么log4j。什么是log4j呀?
当然,大致的思路总归差不多的。因为我的neptune系统只需要一个logging的工具,而不关心这个logging工具是什么,这就是一个perfect的依赖注射的场合。
先定义接口Logger,然后从构造函数传递近来一个Logger实例,接着就直接调用Logger就是了。
print用来输出信息,但是不折行,println可以折行。
logException用来直接纪录异常。这样,对异常是直接printStackTrace,还是println(e.getMessage())就是由具体的Logger实现来决定,我的neptune只需要把遇到的异常报告给Logger就是了。
好。接下来我吭哧吭哧地把neptune完成了,剩下的就是从哪里找一个Logger实现了。
最简单的Logger实现自然就是直接往屏幕上打印了:
直接把这个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。
设计几个简单的基本粒子,几个简单的相互作用力,然后让这些东西自己组合,随意发展,不是比事必躬亲,先想透彻了自己想让世界是什么样子,然后一张一张图纸地具体设计,一个一个人地造,
“撒旦,你去干坏事,往死了整这帮贱人!”
“天使,你去干好事,打个巴掌再给他们点甜枣吃。”
“儿子,你下去混混,看丫敢不敢钉死你!”
来的容易美妙?
哈。终于形而上起来了,爽!
这几天在完善我的neptune系统和jaskell语言。顺手发现了一个logging的需求。如获至宝阿。
为什么呢?不是因为这个需求多么难,或者我的解决方法多么巧妙,而是因为,这个例子足够简单,直观,要说明它,背景知识几乎不大需要,三两句话大家就明白需要达到什么效果。这种例子可不是随便就想得到的。
而同时,它又对实现提出了一定程度的灵活性要求,正好方便我展示我叫做“面向组合子”的程序设计方法。
说到这里,不禁又有点沮丧,我也挺想象别人那样,先高举高打,玄之又玄,弄些哲学思辨,什么佛法呀,道德经阿,西游记亚,以及各位西方先哲的亟语,甚至量子力学的悖论。这样才能吸引眼球,增加人气呀。
可是,等而下之的工匠气作祟,说着说着就要拐到具体例子上去了。不争气呀。
算了,不想那么多了。论道不是俺这种俗人所擅长的,还是鼓捣“器”吧。
大致的背景是这样:
我的neptune是一个build system,在build的过程中会产生很多log信息。这些信息分为不同的重要级别。
说到这里,肯定有人已经按奈不住:用Log4j!
先不要急,我们这里不是要告诉你怎么处理你得程序中的logging需求,而是要通过这样一个容易理解的例子来说明以下“面向组合子”的编程方法。所以,这里让我们先假设我们不知道什么log4j。什么是log4j呀?
当然,大致的思路总归差不多的。因为我的neptune系统只需要一个logging的工具,而不关心这个logging工具是什么,这就是一个perfect的依赖注射的场合。
先定义接口Logger,然后从构造函数传递近来一个Logger实例,接着就直接调用Logger就是了。
public interface Logger{ void print(int level, String msg);; void println(int level, String msg);; void logException(Throwable e);; }
print用来输出信息,但是不折行,println可以折行。
logException用来直接纪录异常。这样,对异常是直接printStackTrace,还是println(e.getMessage())就是由具体的Logger实现来决定,我的neptune只需要把遇到的异常报告给Logger就是了。
好。接下来我吭哧吭哧地把neptune完成了,剩下的就是从哪里找一个Logger实现了。
最简单的Logger实现自然就是直接往屏幕上打印了:
class SimpleLogger implements Logger{ public void print(int lvl, String msg);{ System.out.print(msg);; } public void println(int lvl, String msg);{ System.out.println(msg);; } public void printException(Throwable e);{ e.printStackTrace();; } }
直接把这个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。
设计几个简单的基本粒子,几个简单的相互作用力,然后让这些东西自己组合,随意发展,不是比事必躬亲,先想透彻了自己想让世界是什么样子,然后一张一张图纸地具体设计,一个一个人地造,
“撒旦,你去干坏事,往死了整这帮贱人!”
“天使,你去干好事,打个巴掌再给他们点甜枣吃。”
“儿子,你下去混混,看丫敢不敢钉死你!”
来的容易美妙?
哈。终于形而上起来了,爽!
推荐阅读
-
论面向组合子程序设计方法 之 创世纪 OOlog4j编程Websphere脚本
-
论面向组合子程序设计方法 之 微步毂纹生 设计模式OO八卦脚本音乐
-
论面向组合子程序设计方法 之 燃烧的荆棘 IOCOOXP
-
论面向组合子程序设计方法 之 创世纪 OOlog4j编程Websphere脚本
-
论面向组合子程序设计方法 之 新约 设计模式OOFP交通Websphere
-
论面向组合子程序设计方法 之 燃烧的荆棘 IOCOOXP
-
论面向组合子程序设计方法 之 失乐园 之补充 OOXPTDD算法
-
论面向组合子程序设计方法 之 重构 设计模式OOIOCBean配置管理
-
论面向组合子程序设计方法 之 oracle OracleOOAntSpringHaskell
-
论面向组合子程序设计方法 之 重构2 OOCC++C#IOC