其实一共也就才2天。

还是头儿一直张罗着要做一个设计模式的培训,因为连续2次公司技术考试里大部分人对设计模式的试题回答得都比较业余。我一直觉得这种东西是很虚的那种技术,所以一直以来不是太感冒:曾经有过N次都是看了设计模式的书就发现自己前几天在干的东西就是某模式,总觉得一帮人吹的那么神乎其神干什么。今天讲师也正好提到了Joshua Bloch大爷也有这种经历。所以说,自然而然地在不了解模式的情况下就应用了一种或N种模式也不是什么稀奇的事情,更不值得沾沾自喜,只不过不学习设计模式就能自己总结出目前流行的所有这些设计模式,那就真是神一般的存在了。

今天想记一笔,主要是因为有一个题目有点儿意思,直接就和讲师想到一起去了(过分低调了,否则可以拿个小奖品),而且应该比他想的还多一丁点。

题目很简单,设计一个logging框架,要求主要是:

  1. 支持同一笔log记录到不同的目的媒体,比如文件、数据库,或者通过socket传输字节流;
  2. 支持同一笔log记录到不同目的媒体时使用不同的格式;
  3. 支持log分级;
  4. 除了将log级别作为是否记录log的判别标准以外,要允许用户创建自己的算法逻辑决定log是否记录。

大概就是这个意思了,细节记不太清。

很多人的第一印象是:这不就是一个设计log4j么?!不过仔细考虑的话,1和4都是log4j不能直接支持的;第1个要求可以通过log4j.xml配置方式通过AsyncAppender搞定,但显然这是具体实现类的行为,没法体现框架的设计思路。

类图,草稿,而且不规范

类图,草稿,而且不规范

上面是草草画的一个类图,很草,而且也不规范,箭头的实虚都是后来看了UML类图规范又改的。

在大部分人都直接认为这就是个log4j的设计的时候,我和组里的几个哥们儿还是识别出来明显的差异,然后开始了我们自己的设计,目的很朴素:一定要和log4j不同,否则就不去演示了。

不过log4j在大部分java开发人员思维方式里的先入为主的地位还是很严重的,几个接口方法定义过来一看,还是那么地log4j。这可不行。于是先来统一一下中心思想,直接套一个大概还靠谱的模式:chain of responsibility。其实我心里完全想的是另外一个模式:J2EE核心模式里的intercepting filter。主要因为似乎chain of responsibility最原始的定义是最终链中只有一个节点被执行,而我的想法是把输出log的对象做成chain,这样肯定需要链中有0到N个对象能被执行,虽然可以说是种扩展,不过还是别在讨论严肃话题的时候捣浆糊的好,再者,借助filter就可以很容易地针对log级别和用户自定义逻辑进行过滤,只让满足所有条件的log被最终记录下来。

这个时候log4j给我们的先入为主的印象又作祟了一小下;控制输出log的被定义为类似log4j的Logger——姑且也叫做Logger吧,然后向log4j看齐一下,每个Logger都有一个名字,名字的命名规范和java的包及类名的命名规范类似,做成了树状的实现了继承的结构。这时我和另外一个老哥就有一点儿傻眼了:这成了树了,怎么搞链式调用啊?马上我想到一个补丁:生成一个广度优先遍历,哇,这样似乎又和iterator拉上关系了;并且然后允许客户代码获取没有预先配置好的Logger,默认的log行为是继承父节点的行为,这也比较类似Log4j,然后每一次客户代码获取没有预先配置好的Logger的时候,框架就在其父节点下挂接上这个新的Logger,更新广度有限遍历的iterator,etc.。如此这般,折腾了一会儿以后,我失望地发现,哎,这不还是log4j么,核心的部分基本是换汤不换药的。

好在随即马上又想到一个补丁。以上的思路的中心都是Logger们用命名规范生成出来的一个树状结构,痛苦也来源于此,但是现在已经有了filter的定义,不如换个思路,把记log时所用的Logger的名字一起作为参数传到filter中,然后做一个根据Logger名字来决定是否过滤Log消息的filter。这样一来,让我头疼的树状结构终于被干掉了,对框架来说偷了些巧,而且实现这样一个拿到Logger名字然后做一下字符串操作的filter也远比遍历、维护树状结构容易很多。至少针对这个题目看来,树状的结构没有带来更多的好处。

于是我们team就产生出了2种设计:一种是之前最终演化成和log4j基本差不多的设计,一种是我更喜欢的剥离出Logger名字管理的使用了intercepting filter模式的设计。后者明显更简单一些,没有不必要的处理。事后才反应过来,那个树状组织Logger的方式完全不是必需的,题目也根本没提到,只是作为一个需求更改验证了一下我的想法能处理这种组织方式。

讲师在看了其他team的设计之后也公布了一下他的设计,据他说那是97、98年左右的事情了,那个时候他也是刚毕业,和项目经历一起花了一个晚上重构他之前的臭代码到这个设计。98年左右,国内是肯定没有引进设计模式这本书的了。

之后也有其他很多同事“攻击”了chain of responsibility,指出这个模式和这个题目的要求似乎不太搭调,毕竟没人要求必须按照某个顺序记录同一笔log,而observer模式应该更适合一些。其实这个问题用observer的缺陷是很明显的了,filter逻辑完全要再借助其他类实现。无所谓了,好在我提到是用intercepting filter,而不是chain of responsibility,如果照讲师的说法,chain of responsibility带个filter,那observer带个filter不也可以解决同样的问题么。

是以为记。比较意识流了一些,嗯。想想这样一个logging框架,做出来应该也是有一定用途的吧。

作业是一个异常处理框架。实在有些想用上AOP,不知对付OO设计模式的题目算不算作弊乜?