泛型编程在Delphi中的实现(200分)

  • 主题发起人 左轻侯
  • 开始时间

左轻侯

Unregistered / Unconfirmed
GUEST, unregistred user!
多谢几位赐教!
dream-soft说的是,类型安全性确定是用RTTI实现的GP的一个大问题
我对GP所知不多,还得赶紧加油补课……
 

人在边缘

Unregistered / Unconfirmed
GUEST, unregistred user!
对此,我是六窍已通,呵呵!
Listen。。。
Study。。。
Think。。。
 
T

tseug

Unregistered / Unconfirmed
GUEST, unregistred user!
如果想在Delphi之中实现GP,是不是可以考虑采用预编译的方法。这样一来,
对于简单类型和类型安全性的难题也就不复存在了。而且,这样产生的代码效率
会比较高。
 
C

creation-zy

Unregistered / Unconfirmed
GUEST, unregistred user!
左大虾:
我有一个问题:Delphi中有TClass,可以存放类型信息,我想由此实现类似模板的功能。
比如:

TA=class
public
procedure Show
// Output 'A'
end;
TB=class
public
procedure Show
//Output 'B'
end;
TMy=class
CInfo:TClass;
Obj:TObject;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
My:TMy;
begin
My:=TMy.Create;
My.CInfo:=TA;
My.Obj:=My.CInfo.Create;
TA(My.Obj).Show
//为什么此处不能写My.CInfo(My.Obj).Show
??
My.Free;
end;
 

卷起千堆雪tyn

Unregistered / Unconfirmed
GUEST, unregistred user!
1)GP从其本质来说是算法与数据的分离,在面向对象语言中,它一般通过将一系列抽象的
算法类来完成,并同时定义了算法类所处理的数据类。从这个角度上来说,它并不一定
要利用模板才能实现。
2)模板的本身是对类的处理,而不是对对象的处理。所以模板为GP的实现提供了最佳的
支持。
3)单根继承的Serialization比较容易实现,多重继承的Serialization更体现了面向对象
的本意。这个话题其实说深了就是多重继承是否合适的问题,不仅限于Serialization。
多重继承我认为要比单根继承更加合理,只是由于多重继承往往不容易被使用者掌握,
所以很多面向对象语言都采用了单根继承。
4)Delphi的RTTI功能是不错,但RTTI和GP完全是两回事,不存在谁能替代谁的问题。
5)运算符重载和GP也是两回事。GP可以重载运算符来实现,也完全可以通过函数重载来
实现。其实运算符重载并不能实现比函数重载更多的功能,它的作用只是让程序看起来
显得“似乎”简单些。
6)Delphi完全可以实现GP,只是它的实现不如C++的实现好用:)。要完整地实现GP,
我觉得有至少要能够实现模板,对于多重继承勉强可以通过接口或对象组合来部分地模拟。

以上观点偏颇之处,请左兄指教,^_^
 

荷塘新月

Unregistered / Unconfirmed
GUEST, unregistred user!
何谓泛型编程(Generic Programming)?请高手给个解释可否?
 
D

dream_soft

Unregistered / Unconfirmed
GUEST, unregistred user!
To 卷起千堆雪:
"GP从其本质来说是算法与数据的分离"。
我不能同意你的这个观点。GP从其本质来说该是将数据类型做为参数处理,进而产生
算法与数据类型分离的结果。

GP仍是在不断发展中的事物,它不断的在引入新的概念和新的运用方式。并可能对现
有的设计模式发生巨大的影响。我们不可能仅仅满足于在OO方式中用特殊技法来模拟它,
必须督促Borland直接添加GP支持才是正解。
 
M

mikedeakins

Unregistered / Unconfirmed
GUEST, unregistred user!
同意“卷起千堆雪”,不同意“dream_soft”。
GP 需要多重继承,需要模板,按照 pascal 罗里八嗦的语法,鬼知道要添加几个关键字。
borland 现在自己现有的东西都 debug 不过来,还添加新特性?
 
T

tseug

Unregistered / Unconfirmed
GUEST, unregistred user!
同意dream_soft的观点.
>GP仍是在不断发展中的事物,它不断的在引入新的概念和新的运用方式。并可能对现
>有的设计模式发生巨大的影响。我们不可能仅仅满足于在OO方式中用特殊技法来模拟它,
>必须督促Borland直接添加GP支持才是正解。
 

左轻侯

Unregistered / Unconfirmed
GUEST, unregistred user!
http://www.csdn.net/develop/article/10/10694.shtm
在csdn也有很多精彩的发言,大家可以去看看:)
 
Y

yysun

Unregistered / Unconfirmed
GUEST, unregistred user!
粗看了一下 Generic Programming。谈点拙见。

Generic programming 广义上讲是:对通用概念和相关实例进行抽象。
而狭义上讲就是 C++ 用利用模板(template)机制实现代码重用。
实现代码重用的方法很多:除了模板,还可以用类库+RTTI等。

C++中用模板的这种方法脱颖而出的原因是:
1、它是编译期技术,这种方法出来的代码效率很高。
2、C++ 版本繁多、模板比类库更加通用,更加容易得到不同编译器的支持。
3、C++ 的祖师爷把模板作为实现C++旨在出效率的重要手段。
http://www.delphibbs.com/delphibbs/dispq.asp?lid=642717

C++ 可以说已经被各路新的语言四面夹击。Object Pascal,Java 等在类库方面早已可以
和 C++ 媲美,甚至还有 interface、property。C++想生存,必须要维护的特色就是:效率。
而其模板就是其他语言没有的法宝。

Delphi/Java 中没有模板,完全可以用类库来实现代码重用,例如 Jdk1.2中的 collection
framework,Delphi 中的 TCollection 等。如果硬是用 Delphi 的 RTTI 或者 Java 的
Reflection 来模拟 C++ 的模板得话,则:
1、意义不大:Delphi/Java 并没有 C++ 那么多的编译器版本需要兼顾。
2、作用不大:总不会比编译时候就嵌进去替换了的代码效率高。

Delphi/Java 中还是目前还是需要认认真真的类设计。通过设计模式提高对象的可重用性。
如果一定要达到 C++ 的效率,则必须需要语言/编译器的直接支持。
Java 最早也要在 2003 的 JDK 1.5 中才支持 Generics。
http://jcp.org/jsr/detail/14.jsp
http://www.ociweb.com/javasig/knowledgebase/2001May/JavaGenerics.pdf
Object Pascal 什么时候支持模板还不知道。

所以,我看革命性是谈不上的,甚至理论性都谈不上,只能算技巧性的东西。
 
Y

yysun

Unregistered / Unconfirmed
GUEST, unregistred user!
再谈一点名词翻译。不知道为什么其中文翻译为“泛型编程”,好像有点古怪,让人摸不着头脑。
不如叫“概化编程”, 概化 = 概括(抽象),概化 = 概念化。
Generics 一般定义为: 通用概念的抽象化。Abstraction of generic concepts.
Generic programming 的含义是:对通用概念和相关实例进行抽象。
Generic programming is the abstraction of generic concepts and the subsequent
instantiation. http://www.ociweb.com/javasig/knowledgebase/2001May/
也有人把它比喻为“对概念的编程”My working definition of generic programming
is "programming with concepts," where a concept is defined as a family of
abstractions that are all related by a common set of requirements.
http://www.cs.rpi.edu/~musser/gp/
 
P

PENGS

Unregistered / Unconfirmed
GUEST, unregistred user!
只同意yysun,的观点

在Delphi中应用Generic programming ?
不如不谈。

左兄提出些问题,具体用意何在?

http://home.camelot.de/langer/Articles/OOPvsGP/Introduction.htm
http://www-ca.informatik.uni-tuebingen.de/dagstuhl/gpdag.html
http://www.cs.rpi.edu/~musser/gp/


 
A

Alongsun

Unregistered / Unconfirmed
GUEST, unregistred user!
哪位老大做过COM服务器。请指教!!!!!!!!!!分数好商量!!!!!
我想做多层结构的数据库系统。。。。。。

我不知道怎么写COM服务器,我能不能写出来像在BCB里封装的控件那样呀!!!
有没有例子可以让小弟看看!!!
分数可以商量!·!!!
 
F

flier

Unregistered / Unconfirmed
GUEST, unregistred user!
我不完全同意yysun的看法。范型编程的意义并不是只在于效率。
无可非议,效率是非常重要的,但并不是判定的唯一标准,在Delphi
的很多应用范围里,效率并不是首要考虑因素。计算机发展到今天,
软件的发展速度远远被硬件甩在后面,与其担心那些效率问题,不如
花更多精力将逻辑实现的清晰明了,毕竟一个系统使用中最大的经费开销
在于软件的维护,而不是硬件。何况这种非算法的优化,真正起到的作用
并不大,也许对特定的程序来说有意义,但对绝大多数的应用来说,程序
的简洁明了,可维护性更重要。
其次是模板。前面很多文章似乎将模板和范型编程混为一谈,而实际上
模板只是实现范型编程思想的工具而已。C++和Java, Delphi不同,没有
一个单根类结构,因此无法方便地以RTTI实现类型的检测。对C++来说,
模板也许是最好的解决方法,他具有效率高,编译时类型检测等等优点;
但对于其他语言来说,模板并不是唯一的解决方案。只是现在C++以模板
成功地实现了范型编程的思想,谁又能说范型编程不能以其他方式实现?
至于范型编程,我以为他是一个完整的思想体系,并不仅仅只是一种技术,
他以与OO不同的角度看待问题域,通过将动态的操作元素集和静态的算法分离
来达到抽象的目的,他的优势在于抽象出了真正具有普遍性的行为,这种行为
的对象是无差别的操作元素,不管是模板中的T也好,单根类树中的ADT也好,
都只是这种思想的体现而已。
因此,我以为不论以什么方式实现,只要能体现出范型编程思想的这种
操作对象类型无关性,就可以说他是遵循范型编程思想。使用遵循这种思想的
程序,就取得得到范型编程的优势。去争论什么效率,争论该用什么技术不该
用什么技术是无意义的,最终程序员需要的是从这种思想或技术中获得生产力
的提高。其间肯定要付出一些代价,如使用模板时程序编写、调试的复杂性;
运行时使用RTTI后效率的降低等等。但只要这种代价小于使用后的收益,我想
大家就都会乐意使用的。
下面是我从DeCal文档里摘出的几个使用范型编程的例子,看过这几个例子
你就会认识到去争论什么应不应该实现、能否实现都是毫无意义的,现在关键
在于如何去实现,如何既能体现范型编程的优势,又能在效率上最优,
这才是实际问题。

例1:在选修两个课程的学生中找出成绩80分以上的学生,并按名字排序

Procedure test;
Var
class1, class2 : DMap;
GoodStudents : DArray;
I : Integer;
Iter1, Iter2 : DIterator;
Begin
// fill our classes with random students and grades
class1 := DMap.Create;
class2 := DMap.Create;

for I := 1 to 25 do
begin
class1.putPair([Random(100), RandomName]);
class2.putPair([Random(100), RandomName]);
end;

goodStudents := DArray.Create;

iter1 := class1.lower_bound([80]);
iter2 := class2.lower_bound([80]);

setIntersectionIn(iter1, class1.finish, iter2, class2.finish, goodStudents.finish);
reverse(goodStudents);

PrintContainer(goodStudents);

FreeAll([class1, class2, GoodStudents]);
End;

想想如果用TList,你实现这些功能所需的代码量、复杂度、可维护性,
谁敢说这不是范型编程优势的体现?以范型编程思想开发的程序,将远远优于普通程序。

当然,C++在范型编程方面远远领先于其他语言,DeCal还有很多不足之处,
Delphi语法有其固有的限制(但绝对不要小看他),但前途是美好的。

btw:我这两天正在考虑范型编程思想Delphi实现的一些思路,大概写了一两千行代码,
尝试了两三种思路,不知哪位也在实际动手,我们可以互相探讨一下?
 
Y

yysun

Unregistered / Unconfirmed
GUEST, unregistred user!
谢谢 flier 指正,您说得非常好。我们可以从您回答中总结出以下观点:

1、泛型编程的思想:数据和算法分离 (第3、4段)
2、泛型编程是“与OO不同的角度看待问题”(第3段)
3、程序的可维护性比效率更加重要。(第1段)

这些大家基本都已经理解了。现在关键要证明:在 Delphi 中用泛型或者模拟泛型比
用 OO 的方法优越在什么地方,以至于非用不可。否则,我觉得没有必要为泛型而泛型。

我认为用 OO 的方法可以做得比泛型代码量小、复杂度小、可维护性高!
比如:您上面的例子:在选修两个课程的学生中找出成绩80分以上的学生,并按名字排序,
用 TStringList 可以比您那段算法:

Type
TStudent = class
Name: string,
ScoreOfMath, ScoreOfEnglish: integer;
constructor Create(aName);
function AmIGood(): boolean;
end
var StudentList: TStringList;
AStudent: TStudent;
ii : Integer
begin
StudentList := TStringList.Create;
for ii:=1 to 25 do begin
AStudent := TStudent.create(RandomName);
AStudent.ScoreOfMath := Random(100);
AStudent.ScoreOfEnglish := Random(100);
StudentList.AddObject(AStudent.Name, AStudent)
end;

for ii StudentList.count - 1 downto 0 do begin
if not TStudent(StudentList.Objects[ii]).AmIGood then StudentList.Delete(ii);
end;
StudentList.Sort;

SomePrintProcedure (StudentList);
StudentList.SaveToFile ....
freeResources ...
end;

怎么看都是 OO 的方法容易懂,好维护。而且 TStudent 更可以派其他用处呢。

我倒是很有兴趣,有谁愿意出个题目,大家分别用 C++ 的泛型,Delphi 的模拟泛型,
以及 Delphi 的 OO 分别编程,来比较一下 代码量、复杂度、可维护性,甚至效率。
 
D

dream_soft

Unregistered / Unconfirmed
GUEST, unregistred user!
To yysun:
我来出个小题目吧。如我上文所说,Pascal的容器类并非类型安全的。使用AS操作符
的实现,"则是绕过了编译器的检查,将保证类型安全的责任交到了程序员的手中。"
我想请yysun给出一个类型通用并且类型安全的TList类实现。[:)]
 
Y

yysun

Unregistered / Unconfirmed
GUEST, unregistred user!
To dream_soft: "类型通用并且类型安全" 可以用接口来实现。
TxList 只接收实现了特定接口的对象不就安全了。
 
F

flier

Unregistered / Unconfirmed
GUEST, unregistred user!
To yysun:

程序的可维护性、可重用性不是通过十几行的程序能够表现出来的,
有一定编程功底的人,在一定时间内对几千行代码可以非常熟悉。
但是,你要考虑到不是所有维护人员都像你那么有经验,不是所有维护人员
都有时间把代码重读一遍。
如果代码写给自己看,我和你一样,喜欢用一些效率最高,哪怕使用
非规范技术;但作为一个项目来看,程序就应该以可维护性第一的观点来编写
你的代码的确没有问题,可是如果现在程序交付已经半年甚至更长,
现在需求发生变化,学生扩容了,学生列表需要用效率更高的方式存储,
如Hash表等关联型容器,你都用TStringList实现的代码维护起来代价就非常大
或者如果现在排序的方法发生了变化,你就得从TStringList里面继承一个新类
override排序的sort方法,此时对你的代码的改动也是巨大的,而随之而来的
可能引发的隐藏问题更多,比如你其他程序以与容器相关的算法使用了这个列表
而你没有发现。如果有自动的单元测试还好办一点,如果无法进行回归测试,
修改、测试往往只集中于一小点,这是隐藏的问题带来的风险是非常大的。
而且类型检查在你的代码里,是以一种逻辑上的方式进行的,也就是说
由程序员说了算,这本身就是一种潜在的问题,因为人总是会出错的,只有
尽可能缩小限定范围,才能最大程度上减少因为人的因素带来的风险。
范型编程最大的好处,就是能在某种程度上降低这些风险,他将程序中
静态的Concepts抽象出来,提供一个具有普遍性的算法集。使算法与容器
无关,与容器的元素无关。
至于出题比试,呵呵,我倒是也愿意用C++来写,毕竟现在C++在范型编程
方面的发展超出其他语言太多,Delphi的范型编程思想的运用,目前为止也只是
在讨论中,虽然有DeCal之类的成功范例,但其本身也有很多不足之处,比如
DeCal里很多行为的归属不是很清晰,为了实现方便混淆了一些概念等等,
我现在还没完全把他看完,自己的范型编程库也只是处于尝试阶段,因此如果比试
我想除非写个几千行而且编写途中、或者写完需求还发生变化的程序,否则无法
体现出目前范型编程的优势。但不排除以后Delphi可以在这方面做的更好,
起码我现在就发现了好几个可以比DeCal中某些部分更好的解决办法。
如果大家一起来努力,我相信可以让范型编程思想在Delphi编程中得到更好的体现。
你文中有一句话“没有必要为泛型而泛型"我觉得深有同感。
这首先需要有对范型编程的足够的了解,通过我们的讨论,相信大家能够互相学习进步。
其次需要有好的支持环境。比如C++现有的不同版本实现的STL库,功能、效率上
的差别就很大,如果Delphi没有这样一套优秀的支持库的话,范型编程也只是空谈,
或者只值得在某些局部得到实行。我是个实用主义者,只有使用这种思想能够得到收益,
或者说收益大于代价时,我才会去使用。
最后,使不使用这样一个思想体系,很大程度上是取决于你对程序的观察角度的。
比如作为一个普通的程序员,你看到的只是程序的一个模块甚至一个函数,你可能觉得
用OO甚至函数实现一样可以,没有必要去用范型;如果作为一个项目维护人员,我想程序的
可维护性绝对是第一位的,你没时间、没兴趣或者可能根本看不懂别人的程序。如果用范型编程,
起码可以很大程度上减轻维护的负担;如果作为一个项目管理人员,你关注的因素可能更多,
比如使用范型编程这种新思想体系本身给项目带来的风险,比如使用范型编程对项目效率影响
与可维护性影响的比较等等。从不同的角度去看待一个问题可能得出不同的结论,对观察者
来说都是正确的,但对整体来说可能是错误的。因此我觉得是否使用范型编程或者其他一些思想、
技术,应该以综合的、整体的、全局的观点来考虑,不应该仅仅执着于效率、类型安全、可维护性
等等某个点,否则只是盲人摸象,以偏概全而已。
因此,我在前面的论述中,基本上也只是集中于范型编程在delphi中实现的可能性、
优势、以及范型编程的一点观念性问题的争论,我好像没有说过必须要用范型编程,呵呵
如果自用软件,或者像你那样十几行,几十行甚至千余行,用什么技术是你的自由;
如果是一个完整的项目,特别是需要和别人合作,需要有后期维护,需求会发生变更的情况下,
范型编程就可以表现出其优越性,这也是为什么我对范型编程感兴趣的原因。
总而言之,用不用范型编程思想,是具体问题具体分析,实事求是。但是作为范型编程的
技术,我认为是绝对有必要研究、发展的,这也是为什么我在这里发文章的原因。

btw:接口是在Delphi中实现范型编程的工具之一,但纯靠接口是无法实现范型编程甚至
完全类型安全的。我这几天尝试的一个方案就是纯接口架构,结果失败了,呵呵
 
顶部