论坛首页 Java企业应用论坛

论面向组合子程序设计方法 之 微步毂纹生

浏览 87455 次
该帖已经被评为精华帖
作者 正文
   发表时间:2005-08-10  
ajoo 写道
咳。合着你说的只是在原子级别不能有两个原子干同样的事情?这有什么难的?那么多语言,只怕功能在基础设施上重叠的也不多吧?即使有重叠,理论上也可以看作为了方便提供的一个shortcut,没什么大不了的。
而在此基础上的非原子级别的组合上如果一件事可以有两个方法是无所谓的了?那不就没问题了?

这个不是我说的,是正交的直观含义而已。语言设计的正交性是一回事情,应用系统设计的正交性是另外一回事。就这个CO构建应用系统而言,关键是给出的原子组合子的功能上不能有重叠的。这点说难不难,说简单不简单。就好比命题逻辑中有了原子命题和推理规则,可以搞出很多东西,但是,如果碰巧哪一天发现某些所谓的原子命题原来是另外一些的逻辑推演,那就掉链子了.
那个CO logging的设计就没有达到这个要求。举个简单的例子,IgnoringLogger有点多余的味道。以你的水平尚且如此,我等俗人用上CO+正交设计就只能等死了.
不过,我在想,使用CO时难道一定要引入正交才能够算够形而上?
0 请登录后投票
   发表时间:2005-08-10  
引用

你就直接说:就是有,就是有,反正文档都在那呢,自己看去。
不就结了?

就是这句话啊。这里的一层隐含意思是,如果你熟悉log4j,那么我不用给出配置文件的例子,你也知道它能实现。但是如果你不熟悉,我即便给出了例子,你会进一步要求这些配置怎么个实现那些需求的细节解释,最后必然会堕入到log4j的代码中去。
与其这样,不如建议你直接去看文档了。

引用

这个举证责任的问题,就是个皮球,我举证了co可以做,您老说,oo也可以作,不信你举证。噌!皮球就又到我脚底下了。

证据就在log4j的网站上摆着呢,难道非要我写出来的东西才算是证据?就顶头那篇Short introduction to log4j,如果你相信这个家伙写的是真的,那自然就解决了。

引用

我原来怀疑的,是你对我那些需求是不是仔细看了。因为从你的几个回答来看,总是觉得答非所问,让我不得不怀疑你只是想当然地认为有。

这个只是因为你基本上不了解og4j,所以我说什么,你都希望有进一步的说明。所以才建议你去看看基本的文档,否则真的是没法说。我有充分的理由相信你没有运行过我前面给出的那个SimpleLayoutIgnoreException.java和相关的那个配置文件,只要使用一下,后面有些话就可以免掉了。

引用

不过算了。log4j有没有某个功能对我也不重要。我又不是要做一个跟它竞争的logger,一个演示而已,何必较真?我就当它有好了。

hehe,这个态度就对了,本来就是一个演示而已
0 请登录后投票
   发表时间:2005-08-11  
引用
不过,我在想,使用CO时难道一定要引入正交才能够算够形而上?

就是这话。说实话,我真不觉得单纯追求“正交”而正交有什么意义。人不是神仙,设计几个基本组合子,后来发现互相可以推导,又so what?影响什么了吗?


比如那个map,如果某个人不熟悉monad,那么很可能屁颠屁颠儿地把map当个宝儿,认为再基本不过了。过两天,惊讶地发现bind可以简单地推演出map。

可是,这个发现并不会产生任何坏的影响。你可以选择重新设计map,用bind组合,也可以就放在那里。我是觉得,单独写一个map,比用bind推导,可理解性更强,而付出的额外代价很小,所以还不错。


co可以xp,允许犯错误,允许不完美。

就比如那个ignore,我前面也说了,完全可以用一个predicate来抽象,可以和filter统一起来的。但是,凡事都是利弊权衡的。既然认为好处不足以抵消复杂性的代价,就算了。不过,这并不影响以后我发现更多的predicate的情况,发现抽象变得值得了,就refactor。
我的timestamp其实也不是最简单。要是想实现readonly提的那个打印行号,基本上就得和timestamp一起重构一下,抽出一些共性来。

其实这些抽象,要是在haskell, jaskell里面,我可能上来就做了。但是java语言让做这些抽象的门槛提高了,权衡之下,就放弃了,也没什么。
0 请登录后投票
   发表时间:2005-08-11  
ajoo 写道
比如那个map,如果某个人不熟悉monad,那么很可能屁颠屁颠儿地把map当个宝儿,认为再基本不过了。过两天,惊讶地发现bind可以简单地推演出map。

恩恩,这就说我呢。
0 请登录后投票
   发表时间:2005-08-11  
ajoo 写道
引用
不过,我在想,使用CO时难道一定要引入正交才能够算够形而上?

就是这话。说实话,我真不觉得单纯追求“正交”而正交有什么意义。人不是神仙,设计几个基本组合子,后来发现互相可以推导,又so what?影响什么了吗?

当然没有影响,这只是说明CO采用的这种渐进设计并不能够有效地确保正交性,而且很可能无意识的破坏。

引用

比如那个map,如果某个人不熟悉monad,那么很可能屁颠屁颠儿地把map当个宝儿,认为再基本不过了。过两天,惊讶地发现bind可以简单地推演出map。

这也没什么,一个系统的原子和推演规则是"定义"出来的,构建者所需要的,只要确保在这个已经定义的系统中原子的不可分解性就可以了。至于系统之外它可以被谁推演,这本来就是无关的事情。

引用

co可以xp,允许犯错误,允许不完美。

允许不完美是对的,但是不要因为存在重构这个手段,就可以抛开CO设计的简洁强大,而去允许丑陋。说句实话,那个logger接口在logging语境下,就只能是"丑陋"两个字。

引用

就比如那个ignore,我前面也说了,完全可以用一个predicate来抽象,可以和filter统一起来的。但是,凡事都是利弊权衡的。既然认为好处不足以抵消复杂性的代价,就算了。不过,这并不影响以后我发现更多的predicate的情况,发现抽象变得值得了,就refactor。

犯错误也要看代价。就比如之前的那个print的引入,如果系统里面已经有了几十个上百个(用CO的方法,这是一个非常容易达到的树木)组合子,突然有一天因为某个原因需要在这些组合子的接口中添加一个方法,只能晕倒了。接口的重构在OO系统里面已经是一件需要非常谨慎的事情,在CO的组合子世界里面,组合子接口的重构,绝对是一件伤筋动骨的大事。
还有,你有没有这个接口已经很难看了,暴露太多:
  public void print(int level, String msg);;
   public void println(int level, String msg);;
   public void printException(Throwable e);;

有了print,println就很尴尬。而且这个print只是内部需求驱动的(实现timestampLogger的需要),在logging这个语境下面,对外部使用者而言并没有价值,和println放在一起,会给使用者造成不必要的混淆。
比较面向实现的办法是保留接口是搞一个抽象基类,把println设计成为print的组合。然后大伙组合子都继承这个基类就可以了。如果将来又出现那种需要内部组合的情况,直接在基类里面搞,犯不着一个一个的去努力。
但是,在CO里面,其实接口变成
  public void print(int level, String msg);;
  public void printException(Throwable e);;

这个就可以了。直接定义一个打印行分隔符的logger不就结了,配置的时候装配去吧。喜欢分行的分行,什么时候分行都可以。对于调用者而言,这个print分不分行,怎么分行,完全是自己在配置的时候定义的。
另一个不停引入原子组合子的风险是,遵循这个方式设计的CO应用系统的核心部分(由那些原子组合子组成)一直是在增长的,而且以一种冗余的方式增长。要用引入原子组合子来抵消复杂性,本身就是设计思路上的一个短板。我在想CO里面是不是存在定义静态中间结构的办法,比如定义一个分子组合子,达到内部复杂性和接口简洁的平衡。
0 请登录后投票
   发表时间:2005-08-11  
charon 写道

引用

co可以xp,允许犯错误,允许不完美。

允许不完美是对的,但是不要因为存在重构这个手段,就可以抛开CO设计的简洁强大,而去允许丑陋。说句实话,那个logger接口在logging语境下,就只能是"丑陋"两个字。


这个接口设计确实可以有商榷的余地,比如rreadonly说的printException也应该有level。

charon 写道

引用

就比如那个ignore,我前面也说了,完全可以用一个predicate来抽象,可以和filter统一起来的。但是,凡事都是利弊权衡的。既然认为好处不足以抵消复杂性的代价,就算了。不过,这并不影响以后我发现更多的predicate的情况,发现抽象变得值得了,就refactor。

犯错误也要看代价。就比如之前的那个print的引入,如果系统里面已经有了几十个上百个(用CO的方法,这是一个非常容易达到的树木)组合子,突然有一天因为某个原因需要在这些组合子的接口中添加一个方法,只能晕倒了。接口的重构在OO系统里面已经是一件需要非常谨慎的事情,在CO的组合子世界里面,组合子接口的重构,绝对是一件伤筋动骨的大事。

不见得。co里面,往往只有基本组合子才会直接实现这个接口。剩下的派生的,都是组合基本组合子。只要保证基本组合子不要太多就不会有太大的伤筋动骨。


charon 写道

还有,你有没有这个接口已经很难看了,暴露太多:
  public void print(int level, String msg);;
   public void println(int level, String msg);;
   public void printException(Throwable e);;

有了print,println就很尴尬。而且这个print只是内部需求驱动的(实现timestampLogger的需要),在logging这个语境下面,对外部使用者而言并没有价值,和println放在一起,会给使用者造成不必要的混淆。
比较面向实现的办法是保留接口是搞一个抽象基类,把println设计成为print的组合。然后大伙组合子都继承这个基类就可以了。如果将来又出现那种需要内部组合的情况,直接在基类里面搞,犯不着一个一个的去努力。
但是,在CO里面,其实接口变成
  public void print(int level, String msg);;
  public void printException(Throwable e);;

这个就可以了。直接定义一个打印行分隔符的logger不就结了,配置的时候装配去吧。喜欢分行的分行,什么时候分行都可以。对于调用者而言,这个print分不分行,怎么分行,完全是自己在配置的时候定义的。

其实,print/println的命名有点问题。所谓"println"是说打印一条完整的log信息,是否在一行,是实现细节。
而"print",是打印部分log信息。所以,两个函数是必要的。其实,考虑到效率,还应该加上print(int), print(char), print(boolean)等,以避免不必要的boxing。



charon 写道

另一个不停引入原子组合子的风险是,遵循这个方式设计的CO应用系统的核心部分(由那些原子组合子组成)一直是在增长的,而且以一种冗余的方式增长。要用引入原子组合子来抵消复杂性,本身就是设计思路上的一个短板。我在想CO里面是不是存在定义静态中间结构的办法,比如定义一个分子组合子,达到内部复杂性和接口简洁的平衡。

原子组合子只有在开始会增长,到后来就会趋于稳定了。一般来说,十几个原子组合子就差不多够了。
0 请登录后投票
   发表时间:2005-08-12  
论面向组合子程序设计方法 之 重构
http://forum.iteye.com/blog.php?userid=6423
0 请登录后投票
   发表时间:2005-08-12  
看了半天, 我理解ajoo讲的CO是把OO的继承完全扔掉, 完全用接口定义规范, 用组合实现复用.不知道理解的正确与否.
不过你举的例子太象Decorator了, 你说的CO中有没有跨规范的组合? 比如, 你用Java实现的CO中有没有一个类Implement多个接口的时候, 什么情况下用会用? 从你举的例子中, 我觉得不应该有. 但我觉它这很重要, 是不是我的思维有局限?
0 请登录后投票
   发表时间:2005-08-12  
co的关键点是概念推演.
0 请登录后投票
   发表时间:2005-08-12  
donnie_yang 写道
看了半天, 我理解ajoo讲的CO是把OO的继承完全扔掉, 完全用接口定义规范, 用组合实现复用.不知道理解的正确与否.
不过你举的例子太象Decorator了, 你说的CO中有没有跨规范的组合? 比如, 你用Java实现的CO中有没有一个类Implement多个接口的时候, 什么情况下用会用? 从你举的例子中, 我觉得不应该有. 但我觉它这很重要, 是不是我的思维有局限?

一个类implement几个接口无关紧要。co只看interface,不看类。
这些类等于是在co的局部用oo来实现某些东西而已。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics