发现老庄的连载方法很好.又能吸引眼球又能好整以暇.于是从善如流.
这几天在完善我的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。
设计几个简单的基本粒子,几个简单的相互作用力,然后让这些东西自己组合,随意发展,不是比事必躬亲,先想透彻了自己想让世界是什么样子,然后一张一张图纸地具体设计,一个一个人地造,
“撒旦,你去干坏事,往死了整这帮贱人!”
“天使,你去干好事,打个巴掌再给他们点甜枣吃。”
“儿子,你下去混混,看丫敢不敢钉死你!”
来的容易美妙?
哈。终于形而上起来了,爽!
分享到:
相关推荐
本书以面向对象的软件工程思想为主线,细致深入地讲解了C#语言面向对象程序设计的方法和技巧,内容涵盖面向对象的基本概念、基于接口的设计、泛型程序设计方法、Windows和Web应用开发,以及数据库访问技术。...
《Java与UML面向对象程序设计》强调理论和设计相结合,重视对软件开发方法学有指导作用的重要概念。《Java与UML面向对象程序设计》可作为高等学校计算机科学系及软件学院高年级学生和研究生的教科书,也可作为从事...
《Visual C++面向对象与可视化程序设计》是黄维通教授编著的一本经典教材,主要介绍了使用Microsoft的Visual C++编程环境进行面向对象程序设计和可视化应用开发的基础知识。这本书深入浅出地讲解了C++语言的核心概念...
第五、六章叙述循述循环、分支、子程序等基本程序结构以及程序设计的基本方法和技术;第七章为宏汇编技术;第八章说明以中断为主的输入/输出程序设计方法;第九章介绍BIOS和DOS系统功能调用的使用方法;第十~十二章...
1.5 面向对象的程序设计语言 1.6 C和C++ 1.7 简单的C程序介绍 1.8 输入和输出函数 1.9 C源程序的结构特点 1.10 书写程序时应遵循的规则 1.11 C语言的字符集 1.12 C语言词汇 1.13 Turbo C ...
习题集内容覆盖面广,包括:Java言的基本常识、基本语法、面向对象的基本概念、数组、字符串、异常处理、文件和数据流、图形用户界面设计、小应用程序、线程、编程规范、网络程序设计、多媒体民图形学程序设计以及...
全书共13章,主要内容有Python语言基础、顺序结构、选择结构、循环结构、字符串与正则表达式、列表与元组、字典与集合、函数与模块、面向对象程序设计、文件操作、异常处理、图形绘制、图形用户界面设计。...
本文档是一篇关于基于微信小程序的点餐系统设计与实现的毕业论文,旨在利用微信小程序这一日益普及的技术,优化餐厅点餐流程,提供便捷的在线点餐服务。论文详细介绍了系统的开发背景、技术选型、需求分析、系统设计...
Visual C# 2010程序设计教程》详细介绍了Visual C# 2010程序设计的基础知识、基本方法和应用技巧,共分14章,主要内容包括.NET平台与Visual Studio 2010开发环境、C#语言基础及面向对象程序设计、C#程序设计、C# Web...
1. 封装:封装是面向对象编程的核心原则之一,它将数据和操作数据的方法绑定在一起,隐藏内部实现细节,只对外提供公共接口。通过访问修饰符(如public、private、protected),我们可以控制对类成员的访问权限,...
刘金琨滑《滑模变结构控制MATLAB仿真》第3版 基本理论与设计方法pdf+仿真程序 滑模变结构控制本质上是一类特殊的非线性控制,其非线性表现为控制的不连续性,这种控制策略与其它控制的不同之处在于系统的“结构”...
第1篇(第1~10章)为autollsp程序设计基础篇,主要介绍了autollsp的基本结构、语法、功能函数、对象属性、循环、判断式、子程序、选择集、符号表、读文件以及写文件等autolisp程序设计的相关知识与技巧。第2篇(第11章...
本书的作者,jeff prosise,用其无与伦比的技巧向读者讲述了mfc程序设计中的基本概念和主要技术——再次阐释了在32位windows平台上进行了快速的面向对象开发的完美方法。 本书涵盖了以下专题: 事件驱动程序设计...
《C++程序设计教程》(修订版)是钱能教授的经典著作,专注于讲解C++语言的设计思想和实际应用。这本书不仅介绍了C++的基础语法,更深入地探讨了面向对象编程的概念,包括类、对象、继承、多态等核心概念。课后习题...
同时,程序设计思路和流程图也是必不可少的,包括主程序和各子程序流程,这部分通常涉及到软件实现,如键盘设置、显示逻辑等功能。 四、测试方案与测试结果 团队需要提供一套全面的测试方案,包括硬件测试、软件...
C++面向对象程序设计教程习题解答与上机指导陈维兴 (第三版!
《POSIX多线程程序设计》深入描述了IEEE的开放系统接口标准——POSIX线程,通常称为Pthreads标准。本书首先解释了线程的基本概念,包括异步编程、线程的生命周期和同步机制;然后讨论了一些高级话题,包括属性对象、...
### Go语言程序设计 #### Go语言简介 Go语言是由谷歌公司于2009年发布的一种新型编程语言,旨在简化编程过程的同时不牺牲程序的性能。该语言的设计初衷是为了应对过去十几年来软件开发日益复杂的挑战。Go语言的...