精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2005-07-06
age0 写道 OOer们总是喜欢吹嘘“XX重用”,谎言说上一千遍,就成了真理。
对于这种赤裸裸的谎言,简单的面向过程案例就可以将其华丽的轰至渣。 还是解码器的案例,标准的输入输出接口,可置换的解码芯片。 OO设计2 class device { Input();; Output();; virtual Decode();; } class device1 : device { override Decode();; } class device2 : device { override Decode();; } usage: // 解码方案1 device d = new device1();; d.Input();; d.Decode();; d.Output();; // 解码方案2 device d = new device2();; d.Input();; d.Decode();; d.Output();; 使用继承及接口技术,重用了Input、Output的实现及Decode的接口,看起来好象很伟大 设计3 看看面向过程 Input();; Output();; Decode1();; Decode2();; usage: // 解码方案1 Input();; Output();; Decode1();; // 解码方案2 Input();; Output();; Decode2();; 设计2和设计3基本上是等价的,设计1则最愚蠢。在面向过程的设计理念中,Once and Only Once 本来就是最天经地义的事情,同一件事不会去做两遍,OO的继承及接口机制不过是使OOer们能够避免愚蠢设计回归到正常设计而已,犯不着整天挂着“重用”的美名自娱自乐。 可以这么写,就体会出OO语法的优势。 OO设计2 class device { Input();; Output();; virtual Decode();; process();{ input();; decode();; output();; } } class device1 : device { override Decode();; } class device2 : device { override Decode();; } usage: // 解码方案1 device d = new device1();; d.process();; // 解码方案2 device d = new device2();; d.process();; 可以尝试用C写,达到重用process (input, decode, process) 的效果。只能用Callback function pointer 达到类似的效果。 关于这个问题, http://forum.iteye.com/viewtopic.php?t=13954 buaawhl 写道 OOP/AD basic 本文是为了配合庄子的论文,做一些OO的基础知识普及工作。 当然,即使是OO基础知识,我也不一定有足够的资格来普及,主要目的还是为了相互学习,共同进步。因此,为了便于读者找出漏洞,进行批评和抨击,帮助我进步,我尽量使用朴实无华,简单易懂的语言。用咱老百姓自己的话,讲述老百姓自己的故事。 为了突出重点,加强效果,文中不免矫枉过正,提出一些片面极端的看法。 为了方便起见,我也把Procedure Oriented称为PO。 1.代码重用 评价一门语言的重要标准之一,就是代码重用程度。PO的代码重用主要有两种方式:(1) 模块重用 Module Reuse (2) 模板重用Template Reuse 模块重用。比如,一个 a 模块,b调用a,c也调用a。这时候,a就是重用的。这种情况比较简单,PO已经做得非常好了,OO这方面没有什么太大的超越。 模板重用。一个处理流程的整个步骤都是固定的,就是其中一些部分是变化的。比如,在那个Design Pattern帖子里面。 http://forum.iteye.com/viewtopic.php?p=82944 那个排序的例子里面,sort算法是重用的,comparator是变化的。这个时候,sort就是一个模板template,comparator属于模板template中的变量。 OO在模板重用这个方面具有超越PO的语法优势。PO一般用Callback Function Pointer实现模板重用,而OO的Class内置支持this指针,具有了携带额外信息的能力。 2.OOP只是一种语法现象 PO如果要达到OO的效果,则需要用Struct + callback function pointer来模拟实现。C的语法记不清楚了,下面的代码就是一个示意。 struct Comparator{ void * info; int (*compare); (Runnable * this, Record a, Record b);; // compare is a function pointer // 注:具体的OO实现中,为了处理继承的多态, //虚函数指针不是直接放在这里,而是放在一个虚函数表 }; …. void sort(Record[] records,Comparator * comparator);{ for(int i =….);{ for(int j=….);{ if(comparator->compare(comparator, records[i] > records[j]); > 0); // swap records[i] and records[j] } } } 如果要实现ReverseComparator,那么相当麻烦。 int reverseCompare(Runnable * this, Record a, Record b);{ Comparator* originalComparator = (Comparator *);this->info; return – originalComparator->compare(this, a, b);; } …. Comparator field1Comparator; … set up field1Comparator Comparator reverseComparator; reverseComparator.info = &field1Comparator; reverseComparator.compare = reverseCompare; // function pointer sort(records, &reverseComparator);; // reverse order 可以看到,PO实现高级的模板重用,不是不能做到,而是做起来非常麻烦。而OO语法内在就支持这种高级的模板重用。另外,OO语法的继承,对代码重用也有良好的效果,不过,目前继承好像有成为反模式的趋势,我就不多说了。怎么方便怎么用,代码简单,才是硬道理。 |
|
返回顶楼 | |
发表时间:2005-07-06
庄子 写道 我在网上找到了《Modern C++ Design》繁体中文版的前四章PDF文件。果然不出我所料,Loki的设计思路与我的随后将会介绍的自己的设计实现,实有异曲同工之妙。对于C++的熟悉程度超过Java的某同学,可以先去看看这本书,如果能够同意书中的观点,再来与我讨论,相信会得到更多的收获。 我也看了那4章。主要讲解C++ Template的高级用法。 C++ Template主要作用是 (1) 类似于C++ Macro, 用于代码生成。由于C++支持操作符重载,这个功能十分强大。 (2) 编译期静态类型检查。 Java Template 似乎只有第(2)个作用。 那4章讲解的Generic Programming 的高级用法(把具体实现 用类型参数 从外部注入),都可以用 OOP 实现(如果涉及的类型是Class, 而不是primitive type)。只是,OOP 没有GP那种“编译期静态类型检查”的优势。书里面也讲了,两者并不冲突。 ( OOP的实现可能稍微麻烦一些,因为GP相当于编译期的脚本编程,相当于Reflection,约束更小。 比如, p1(A a) { a.f1(); a.f2();} p2(B a){a.f1(); a.f2(); } 这个时候,如果要重用p1, 和P2, OOP要求A 和 B 类型都要实现同一个interface,或者继承同一个class。而且这个interface 或者 parent class 必须有f1(), f2() 两个方法。 而GP 只要求 A 和 B 都必须有 f1(), f2() 两个方法,就可以了。它就可以编译了。 ) 庄子能否深入讲讲,从这4章中有哪些收获? 不只是前面那一段泛泛的“这个不行、那个也不行、只有template才行”的那段文字吧? 庄子 写道 大家都应该承认,ADT是OO的根。数据与操作的封装是一切OO思想的基础,也是所有OO信奉者从来没有怀疑的“前提”! 我没有这个先入之见。也从来不信奉这个前提。:-) 也许我算不上OO信奉者吧。 ADT是 Class (Type) 的理论基础。至于说封装,只是OO的一个feature而已。OO能够很好的支持封装。 我从来都很奇怪,“数据与操作的封装是一切OO思想的基础”,这个根深蒂固的想法是从哪本经典书籍里面来的。可能我没有注意看,基本功没有打好。 庄子 写道 这句话特别关键,让我再仔细分析给大家看看:ADT=抽象数据类型。就是封装了操作和数据的一种用户自定义数据类型。 1、如果仅仅是ADT,那么相近的用户自定义数据类型就无法重用,因此出现了一个数据类型的重用需求; 2、因为ADT封装了对于数据的操作,对于A类数据的操作,就无法被B类数据重用,因此出现了一个ADT内部代码的重用需求; 3、因为对于ADT的操作是借助ADT的外部界面执行的,也就是说,对于接近但是不同的类型的操作,必须写出不同的代码,因此出现了对于操作ADT的代码的重用需求。 这样的分裂的三个需求,在随后的OO的发展历史中,分别被两种方法来初步满足。第一、第二种需求,被继承这样的技术来试图满足;第三种技术被泛型类试图满足。这两个技术满足了三种重用的需求了吗?没有!不但没有满足,而且还带来的诸多麻烦的问题,更在分别满足三种需求的时候,起了冲突。(前面已经讨论过的内容,可以回头再看一看,我将来再改写这篇文章的时候,会将封装 VS. 重用性的分析,作为一根主线贯穿OO分析的始终,现在就不重新组织结构了。) 由于封装与重用性之间,存在着本质性的冲突, 这段话尤其不解。既然封装了,自然是只想自己用,或者家族内部用,为什么还要在外面重用? 如果需要在外面重用,那就单独拿出来好了。 咋就本质性冲突了呢? |
|
返回顶楼 | |
发表时间:2005-07-06
buaawhl 写道 可以这么写,就体会出OO语法的优势。 OO设计2 class device { Input();; Output();; virtual Decode();; process();{ input();; decode();; output();; } } class device1 : device { override Decode();; } class device2 : device { override Decode();; } usage: // 解码方案1 device d = new device1();; d.process();; // 解码方案2 device d = new device2();; d.process();; 单从语法上看好象很有道理,只不过device提供的Input和Output接口是给外部client使用的,不是给device用process来自娱自乐。 无论什么P最终都可以归结为一种语法现象,但语法现象只是外在的技巧性的价值,现在的讨论太过重视技巧性的东西,反而忽略了内在的更高层面的价值。 |
|
返回顶楼 | |
发表时间:2005-07-06
gigix 写道 // 解码方案1 Input();; Output();; Decode1();; // 解码方案2 Input();; Output();; Decode2();; 难度这client的重复代码便不算重复代码了么? 若是有人将这解码器的程序写至你这样,便素该杀亚。 不要光说不练,你可以把包括client在内的完整代码用你拿手的OO语言写出来,我再用面向过程语言来实现相同的实现,最后比较一下最终代码量。OO设计的价值并不在于所谓的“复用”,无论如何设计,最好的结果就是和过程设计等价。 |
|
返回顶楼 | |
发表时间:2005-07-07
age0 写道 buaawhl 写道 可以这么写,就体会出OO语法的优势。 OO设计2 class device { Input();; Output();; virtual Decode();; process();{ input();; decode();; output();; } } class device1 : device { override Decode();; } class device2 : device { override Decode();; } usage: // 解码方案1 device d = new device1();; d.process();; // 解码方案2 device d = new device2();; d.process();; 单从语法上看好象很有道理,只不过device提供的Input和Output接口是给外部client使用的,不是给device用process来自娱自乐。 无论什么P最终都可以归结为一种语法现象,但语法现象只是外在的技巧性的价值,现在的讨论太过重视技巧性的东西,反而忽略了内在的更高层面的价值。 换成Client 可以这么写。 usage: client code process(Device d);{ d.input();; d.decode();; d.output();; } // 解码方案1 device d = new device1();; process(d);; // 解码方案2 device d = new device2();; process(d);; age0 写道 OO设计的价值并不在于所谓的“复用”,无论如何设计,最好的结果就是和过程设计等价。 你给出的例子,恰好属于“模块重用”,用PO (面向过程) 和 OO 都没有什么区别。 在我举出的上述“模板重用”(套用"Template Design Pattern"的说法)的那些Case,PO无法很直观地达到OO的那种“复用”。 再举一个例子。 我前面的帖子给出了那个 排序算法 和 Comparator 的例子。排序算法是重用的,Comparator是变化的。如何用PO实现这种重用? buaawhl 写道 模块重用。比如,一个 a 模块,b调用a,c也调用a。这时候,a就是重用的。这种情况比较简单,PO已经做得非常好了,OO这方面没有什么太大的超越。 模板重用。一个处理流程的整个步骤都是固定的,就是其中一些部分是变化的。比如,在那个Design Pattern帖子里面。 http://forum.iteye.com/viewtopic.php?p=82944 那个排序的例子里面,sort算法是重用的,comparator是变化的。这个时候,sort就是一个模板template,comparator属于模板template中的变量。 OO在模板重用这个方面具有超越PO的语法优势。PO一般用Callback Function Pointer实现模板重用,而OO的Class内置支持this指针,具有了携带额外信息的能力。 |
|
返回顶楼 | |
发表时间:2005-07-07
age0 写道 OO设计2 class device { Input();; Output();; virtual Decode();; } class device1 : device { override Decode();; } class device2 : device { override Decode();; } usage: // 解码方案1 device d = new device1();; d.Input();; d.Decode();; d.Output();; // 解码方案2 device d = new device2();; d.Input();; d.Decode();; d.Output();; 使用继承及接口技术,重用了Input、Output的实现及Decode的接口,看起来好象很伟大 这里问一个题外话,如果java有c++的模板机制的话,客户端的调用就不会调用两次,根据模板方法来写(不好意思,c++不熟悉不知道什么写),不过可以肯定是可以实现的,而java里面有泛型功能,但好像不能解决这个问题(客户端重复调用的问题,使用泛型,好像只有collection那些类可以使用。 |
|
返回顶楼 | |
发表时间:2005-07-07
java为什么不引进模板,
没有模板,很多实现只能用 工厂模式(或者助手类)来实现 |
|
返回顶楼 | |
发表时间:2005-07-07
引用 现在我们有这样一个需求,对于得到的ArryaList,能够一一调用里面的对象的add(int a)方法,当然了,只要这个ArrayList里的对象都是X或者X的子类就行了。我们可以写出这样的代码:
。。。 全文参看http://spaces.msn.com/members/zbw25/PersonalSpace.aspx?_c01_blogpart=blogmgmt&_c=blogpart 里面太经典了,老庄在这一点上说出我的心声了,我刚开始使用泛型的时候,就碰到这个问题,百思不得其解,最后归为泛型的缺陷,一直都郁闷这个问题。 不过好像c++的模板可以解决这个问题吧,但只是猜测,我对c++不熟悉。 |
|
返回顶楼 | |
发表时间:2005-07-07
sigh,我先前说那age0是该杀,这位jarrywen更是该杀。这编码解码的程序应该这样写的么?我给你个提示罢,你不妨想想,MVC的controller(也就是web框架的action),调用它的时候需要先调它一个input、完了以后再调它一个output么?
写出这种挨千刀的程序来,居然还好意思说是OO的问题,居然也还有人附和,sigh。 jarrywen 写道 age0 写道 OO设计2 class device { Input();; Output();; virtual Decode();; } class device1 : device { override Decode();; } class device2 : device { override Decode();; } usage: // 解码方案1 device d = new device1();; d.Input();; d.Decode();; d.Output();; // 解码方案2 device d = new device2();; d.Input();; d.Decode();; d.Output();; 使用继承及接口技术,重用了Input、Output的实现及Decode的接口,看起来好象很伟大 这里问一个题外话,如果java有c++的模板机制的话,客户端的调用就不会调用两次,根据模板方法来写(不好意思,c++不熟悉不知道什么写),不过可以肯定是可以实现的,而java里面有泛型功能,但好像不能解决这个问题(客户端重复调用的问题,使用泛型,好像只有collection那些类可以使用。 |
|
返回顶楼 | |
发表时间:2005-07-07
不好意思,我不知道解码应该什么写
每个人都有局限性,不了解码器并不代表不能谈oo 就像我不了解足球,不能谈罗纳尔多一样 还有,我这里不是附和,而是提出疑问。 在这里我只是想了解和讨论泛型、模板、继承的一些东西 |
|
返回顶楼 | |