锁定老帖子 主题:论面向组合子程序设计方法 之 微步毂纹生
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-08-09
charon 写道 ajoo 写道 charon 写道 引用 co是银弹吗?当然不是,至少以我的功力没有这种自信。 遇到复杂的问题我也是先分解需求,面向接口的。只有问题的规模被控制在了一定的范围,我才会试图用co来解决问题。靠着对co的一些经验和感觉,一旦发现了可以组合子化的概念,成果会非常显著。 那么就是自顶向下分解需求,自底向上构建系统了? 至少在这一点上,和被革命的对象还是有那么一点点相似。 也许可以说, oo在问题规模不大,概念比较单一,但是灵活性要求高的场合(或者说,就剩一块石头了,但是这块石头又大又硬)并不是一个趁手的工具。 比如说,我的yan container,概念很简单,就是创建组装组件。但是同时,对组建的组装要求很高的灵活性,这时就是一个完美的co的场合。 这样的场景,好像某些杂合的动态语言,比如python/ruby之类的,还有那个新来的groovy(很多东西直接套java,不如python来的自然) ,用起来更加顺手一点吧。在单独的这种场景中,我基本不会去考虑看起来是不是那么OO。但是,如果需要集成到大的环境中,可能就需要一些思量了。 用别的语言那就是另外一回事了。其实,如果可能,我也不希望用java来做co的。函数式语言明显更合适。 我说的是设计思路,不是哪个语言。用哪个语言,只要你的概念还是用java实现的,就仍然有设计组合子的必要。这些脚本语言,只是提供一个方便语法的外壳而已。 |
|
返回顶楼 | |
发表时间:2005-08-09
引用 比如,现在有个需求,当logger级别超过一个值的时候必须写到Logger文件中,否则只需要输出到控制台即可;或者是某个级别的log需要单独输出到一个独立的log文件中;
现在你现有的是不够的,必须增加,但是这些需求是你一开始不能料到的 真的? 看着: Logger log_to_file = writer(some_file_writer);; Logger console_log = writer(new PrintWriter(System.err););; Logger mylogger1 = ignore(some_level, log_to_file, console_log);; Logger log_to_special_file = writer(special_file_writer);; Logger common_logger = ...; Logger mylogger2 = filter(some_level, log_to_special_file, common_logger);; 哈哈,正是给我提供素材呢。小样儿,co要是连你这么点小case都对付不了,还更得了呢。 你说我拿着锤子找钉子好说,你说你没事拿钉子找锤子这不是找不舒服么? 不过谢谢你,回头我把这个例子加进去,也正好说明co的强大。(你愿意叫这个东西decorator,随你便,反正如果能更方便你理解,也就行了。对了,decorator和普通多态有什么区别?为什么叫decorator?) |
|
返回顶楼 | |
发表时间:2005-08-09
charon,对付你说的情况,其实最简单的办法是:
自制一个Writer类,这个类不保存output stream,而是每次都重新取得output stream。 但是,logger根本不需要动。 |
|
返回顶楼 | |
发表时间:2005-08-09
ajoo 写道 不知道你觉得用几个简单到家的组合子组合出来那些繁复的需求是不是算“轻松”呢?
hehe,但是从这个logging的例子来看,对于使用者而言,并没有比log4j轻松。 就是说,在已经有一个比较实用的开源实现的情况下,这个logging的例子并不能充分表现出CO的威力来。 引用 我并不想把系统搞得太复杂。现在这个已经够用了。如果有更加变态的需求,也是可以重构的。 不过printException是必要的。仅仅log(String)就失去了对exception特殊处理的能力。 你有什么更好的设计思路吗?不妨提出来看看。 对于logging,我并没有更好的思路,我只是觉得log4j的思路挺好的(不过apache一直在作类似于标准化的工作,hehe)。而且,它的log也可以print stack trace 。它的输出点也可以是任意的,比如可以输出到数据库中。它的内部实现怎么样我不清楚,但是,至少对于使用者而言,接口和配置方式是非常干净自然的。 |
|
返回顶楼 | |
发表时间:2005-08-09
可是这个例子是关注于怎么实现这个需求的亚。
最终的用户友好程度,自然可以做一些封装,弄些配置文件什么的。这不是问题的关键。建筑师关心的是建筑的整体构架,至于最后的精装修,自然有那些decorator,art designer来做。 你还没有说,用log4j怎么做比如说swing说的“如果level如何如何,就写入哪个文件”,以及我前面提到的那些需求呢? |
|
返回顶楼 | |
发表时间:2005-08-09
呵呵,一提起来ajoo就不忘了贬低一下C++,未免也太过小心眼了,这里我不争论什么,说些题外话。
我知道ajoo也是c++好手,你也说自己曾经陶醉过,现在改换java了。喜新厌旧本身人的本性,这也没啥。就好比一个是大老婆,现在老了,缺点也渐渐暴露出来了,小老婆正处于青春年少,自然多受宠爱。但是反过来,我却不知道为何突然改成如此贬低起大老婆,就忘了她曾经的好? 当然我也赞同多学几门语言,毕竟对于男人而言,多占有几个女人不也是成功的标志。 (以上言论,并无侮辱女性的本意,如果有人看到感到不舒服,我表示抱歉) |
|
返回顶楼 | |
发表时间:2005-08-09
雷声大,雨点小啊 。。。。。。。。。。。。。。。。。。
|
|
返回顶楼 | |
发表时间:2005-08-09
ajoo兄,我给你提供一个co的应用场景。我先前在写一个HTML Checker的时候不知道这个叫CO,但是应用了类似的思想,我把它叫做SPAL(Stream Processing Assembly Language)。我发明了一些原子操作,然后用XML配置文件把这些原子操作组合起来。下面就是那个配置文件
<XTidy> <Defaults> <Option name="recursive" value="true"/> <Option name="filter" value="*.html,*.htm,*.xhtml,*.xml"/> <Option name="encoding" value="UTF-8"/> </Defaults> <ParseStage patterns="xtidy.parser.{name},xtidy.parser.{name}Parser,xtidy.commons.consumer.{name}Consumer"> <!-- 解析根元素前面的文本 --> <DEF id="prolog"> <PASS/> <REF id="prolog"/> </DEF> <!-- 解析根元素的内容 --> <DEF id="content"> <OR> <UnfinishedElement names="img, input, meta, br"/> <Element> <Text/> <REF id="content"/> </Element> </OR> <REF id="content"/> </DEF> <REF id="prolog"/> <Document> <REF id="content"/> </Document> </ParseStage> <CheckStage patterns="xtidy.checker.{name},xtidy.checker.{name}Checker,xtidy.commons.consumer.{name}Consumer"> <!-- 检查td元素的内容 --> <DEF id="tdContent"> </DEF> <!-- 检查td元素 --> <DEF id="td"> <AND> <TD checkSize="true" checkRealSize="true" correctSize="true"> <REF id="document"/> </TD> <Element allowAttrs="height, width, bgcolor, colspan"/> </AND> </DEF> <!-- 检查tr元素的内容 --> <DEF id="trContent"> <Element allowNames="td"/> <REF id="td"/> <PASS/> <REF id="trContent"/> </DEF> <!-- 检查tr元素 --> <DEF id="tr"> <AND> <TR checkSize="true" checkRealSize="true"> <REF id="trContent"/> </TR> <Element allowAttrs="height, width"/> </AND> </DEF> <!-- 检查table元素的内容 --> <DEF id="tableContent"> <Element allowNames="tr"/> <REF id="tr"/> <PASS/> <REF id="tableContent"/> </DEF> <!-- 检查table元素 --> <DEF id="table"> <AND> <Table checkSize="true" checkRealSize="true" correctSize="true"> <REF id="tableContent"/> </Table> <Element allowAttrs="height, width"/> </AND> </DEF> <!-- 检查img元素 --> <DEF id="img"> <IMG checkSrc="true" checkSrcExists="true" checkSrcInternal="true" checkSrcPNG="false" checkSize="true" checkRealSize="true" correctSize="true"/> </DEF> <!-- 检查a元素 --> <DEF id="a"> <A checkContentNumber="true" checkHref="true" checkHrefExists="true" checkHrefInternal="true"> <REF id="document"/> </A> </DEF> <!-- 检查form元素的内容 --> <DEF id="formContent"> <OR> <Input checkName="true" checkNameValid="true" checkNameUnique="true" checkCameraPic="true" checkCameraPicExists="true" checkCameraPicInternal="true"/> <Select checkName="true" checkNameValid="true" checkNameUnique="true"/> <TextArea checkName="true" checkNameValid="true" checkNameUnique="true"/> <REF id="element"/> <PASS> <REF id="formContent"/> </PASS> </OR> <REF id="formContent"/> </DEF> <!-- 检查form元素 --> <DEF id="form"> <Form checkName="true" checkNameValid="true" checkNameUnique="true" checkAction="true" checkActionExists="true" checkActionInternal="true"> <REF id="formContent"/> </Form> </DEF> <!-- 检查根元素的子元素 --> <DEF id="element"> <Element forbidNames="style" forbidAttrs="class" info="CSS是不支持的" pass="true"/> <REF id="a"/> <REF id="img"/> <REF id="table"/> <REF id="form"/> </DEF> <!-- 检查文档也就是根元素的内容 --> <DEF id="document"> <OR> <AND> <REF id="element"/> <Width max="200"/> </AND> <PASS> <REF id="document"/> </PASS> </OR> <REF id="document"/> </DEF> <REF id="document"/> </CheckStage> <PackStage patterns="xtidy.packer.{name},xtidy.packer.{name}Packer,xtidy.commons.consumer.{name}Consumer"> <Prolog/> <DEF id="document"> <OR> <A/> <Img/> <Input/> <Form/> <InlineElement names="h1, h2, h3, h4, h5"/> <EmptyElement/> <Element> <REF id="document"/> </Element> <Text/> <PASS/> </OR> <REF id="document"/> </DEF> <REF id="document"/> </PackStage> </XTidy> |
|
返回顶楼 | |
发表时间:2005-08-09
ajoo 写道 可是这个例子是关注于怎么实现这个需求的亚。
最终的用户友好程度,自然可以做一些封装,弄些配置文件什么的。这不是问题的关键。建筑师关心的是建筑的整体构架,至于最后的精装修,自然有那些decorator,art designer来做。 你还没有说,用log4j怎么做比如说swing说的“如果level如何如何,就写入哪个文件”,以及我前面提到的那些需求呢? log4j只能做到 level >= x,则写入 y文件这样的需求. ( !!!这里有个错误,应该是用proerties文件只能做到level>=x,用xml配置可以做到任意连续的级别的输出。) 至于那15个需求,前面10个可以,11-14不存在作的到做不到的问题,比如应该由ajoo你来提供几个Exception类,来打印需要打印的东西(ExecutionTrace之类的),而且,我觉得通过子类化Exception来做额外的trace打印工作,比每个需要特别对待的场景搞一个xxxExceptionLogger要干净。 |
|
返回顶楼 | |
发表时间:2005-08-09
charon 写道 ajoo 写道 可是这个例子是关注于怎么实现这个需求的亚。
最终的用户友好程度,自然可以做一些封装,弄些配置文件什么的。这不是问题的关键。建筑师关心的是建筑的整体构架,至于最后的精装修,自然有那些decorator,art designer来做。 你还没有说,用log4j怎么做比如说swing说的“如果level如何如何,就写入哪个文件”,以及我前面提到的那些需求呢? log4j只能做到 level >= x,则写入 y文件这样的需求. 至于那15个需求,前面10个可以, 那么怎么处理:“如果level>=x写入文件1,同时在屏幕上打印(不过不打印stack trace只打印getMessage()),而不管什么level,都写入一个通用log文件,对exception直接printStackTrace()。”? charon 写道 11-14不存在作的到做不到的问题,比如应该由ajoo你来提供几个Exception类,来打印需要打印的东西(ExecutionTrace之类的),而且,我觉得通过子类化Exception来做额外的trace打印工作,比每个需要特别对待的场景搞一个xxxExceptionLogger要干净。 EvaluationException, NeptuneException都是和logging无关的别的模块抛出的异常。怎么能够让它们和logging的逻辑耦合?如果我调用一个第三方地库,需要在logger里面特殊处理一个XException,难道还要通知这个库地作者改动Exception,因为我的logger需要特殊处理? 更何况,这两个类也没什么可改的。printExecutionTrace()和printStackTrace()都已经提供了,你只需要调用一下就好了。怎么改? 你是说它们应该自动把这些trace打印在printStackTrace()里面?我不这么认为,这样丧失了灵活性。 比如,在我的需求里面,遇到这些exception,在屏幕上打印execution trace/evaluation trace,而在log文件里面打印stack trace。这时就不是override printStackTrace()这么简单的了。 另外,说一句题外话,提意见的也好,觉得自己明白了的也好,真诚建议你们动手做一下这些需求。真正动手做了,才能发现自己哪里想得简单了,才能真正体会co的区别。我费尽心机找的这么清楚的需求例子,别浪费了。 |
|
返回顶楼 | |