论坛首页 Java企业应用论坛

敲响OO时代的丧钟!——DJ对于数据持久化的支持(3)

浏览 192997 次
精华帖 (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语法的继承,对代码重用也有良好的效果,不过,目前继承好像有成为反模式的趋势,我就不多说了。怎么方便怎么用,代码简单,才是硬道理。
0 请登录后投票
   发表时间: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分析的始终,现在就不重新组织结构了。)
由于封装与重用性之间,存在着本质性的冲突,


这段话尤其不解。既然封装了,自然是只想自己用,或者家族内部用,为什么还要在外面重用?
如果需要在外面重用,那就单独拿出来好了。

咋就本质性冲突了呢?
0 请登录后投票
   发表时间: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最终都可以归结为一种语法现象,但语法现象只是外在的技巧性的价值,现在的讨论太过重视技巧性的东西,反而忽略了内在的更高层面的价值。
0 请登录后投票
   发表时间:2005-07-06  
gigix 写道
// 解码方案1 
Input();; 
Output();; 
Decode1();; 

// 解码方案2 
Input();; 
Output();;
Decode2();; 

难度这client的重复代码便不算重复代码了么?
若是有人将这解码器的程序写至你这样,便素该杀亚。


不要光说不练,你可以把包括client在内的完整代码用你拿手的OO语言写出来,我再用面向过程语言来实现相同的实现,最后比较一下最终代码量。OO设计的价值并不在于所谓的“复用”,无论如何设计,最好的结果就是和过程设计等价。
0 请登录后投票
   发表时间: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指针,具有了携带额外信息的能力。
0 请登录后投票
   发表时间: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那些类可以使用。
0 请登录后投票
   发表时间:2005-07-07  
java为什么不引进模板,
没有模板,很多实现只能用
工厂模式(或者助手类)来实现
0 请登录后投票
   发表时间: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++不熟悉。
0 请登录后投票
   发表时间: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那些类可以使用。
0 请登录后投票
   发表时间:2005-07-07  
不好意思,我不知道解码应该什么写
每个人都有局限性,不了解码器并不代表不能谈oo
就像我不了解足球,不能谈罗纳尔多一样
还有,我这里不是附和,而是提出疑问。

在这里我只是想了解和讨论泛型、模板、继承的一些东西
0 请登录后投票
论坛首页 Java企业应用版

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