跟大家一起讨论类与对象! (0分)

K

kk2000

Unregistered / Unconfirmed
GUEST, unregistred user!
type
Tcar=class
private
Fmodel:string;
...
public
procedure go() virtual ;
procedure stop() virtual ;
procedure model() virtual ;
...
protected
...
end;
//从Tcar派生一个公共汽车类出来
type
Tbus=class(Tcar)
public

procedure go() override;
procedure stop() override;
...
end;
//从Tcar派生一个自行车类出来
type
Tbike=class(Tcar)

public
procedure go() override;
procedure stop() override;
...
end;
这是我近来学习到的东西!把我的理解与大家谈谈。
1。首先定义一个"车"的类,这个类是从Tobject继承下来的.
也就是说Tcar=class和Tcar=class(Tobject)的效果是一样的。
2. 在Tcar定义了几个虚拟方法,主要是从车的具有的特性去定义,
因为车都具有”行车“(go)和"停车"(stop)的特性.
这几个虚拟方法主要是为派生类覆盖而设置的。也是使类能够
具有多态的基础;
3. 最后定义了Tcar的两个派生类:Tbus和Tbike,这两个派生都覆盖
基类的虚拟方法.其实这里就是实现"车"的类里面具体每个对象的
的"行车"(go)和"停车"(stop)的方法;比如说就是Tbus是通过发动机
来驱动行车的;而Tbike是通过人的来驱动行车的,也就是“go“的方法;
而“stop“的方法也根据每个派生类的不同而不同.从上面可知这就是类
的多态.
这里主要是说明类的多态是如何实现的,有人说不懂多态就等于
不认识"面向对象",其实上面也有继承,但封装就没有了;
以上均是我个人理解,我不怕错,错了就该。还希望在这里
发表你学习类和对象的一些见解和大家一起分享!
我非常感谢你的参与.

 
没下文了???
 
这是书中的说明!
 
封装是隐藏复杂性。

可能TCar不是你写的,TCar就有可能包含了加速和减速的方法。而这加速和减速方法对于TBus和TBike是一样的,所以,就写在了TCar里。你在后面的TBus或TBike里直接调用加速或减速就行了
 
正巧在我的新书《Delphi面相对象编程思想》中有这个问题的讨论,请参考:
(抱歉图贴不上来)

------------------------------
1.2 面向对象的基本概念
OOP就是使用对象进行编程的过程,所谓对象就是协调数据存储以及作用于数据之上操作的独立实体。对象把数据保存在属性(变量、域、数据成员)中。对象中也包括作用于属性之上的操作,称之为方法(函数、过程、子程)。比如,设想把一辆汽车作为一个对象。一些信息或数据描述了许多汽车中的这一辆的品牌、颜色、最高时速,这些都是汽车对象的属性。汽车也可以完成诸如:启动、加速、行驶、绕行、减速和停车等操作。这些是汽车对象的操作,用的Delphi面向对象语言的术语说,是它的方法。
用户可以通过定义一个对象集合以及它们之间的相互作用来创建一个面向对象程序。总多对象协同工作来定义一个完成用户需要的程序。在本书后面将对面向对象设计过程作深层次的剖析。在这里,只打算让用户先熟悉面向对象的一些概念,并加深用户对Delphi面向对象编程的理解。
1.2.1 类和对象
那么当用户创建一个面向对象程序时,是如何建立对象的呢?可以通过类声明来定义类,然后使用类来创建用户需要的对象。类声明是用来创建对象的模板的抽象规格说明。当用户编写自己的Delphi程序时,所涉及到的主要工作就是编写类声明。当程序运行时,已声明的类用来创建新对象。由类创建对象的过程称为实例化(instantiation)。每个对象是类的一个新实例(instance)。
图 1 4显示了类和对象的不同之处。汽车类是对什么是汽车的一个定义,而解放、红旗和奔驰是对象,是汽车类的实例。

图 1 4类和类对象
1. 属性
类定义中的属性指定了使一个对象区别于其他对象的值。比如,在汽车类的定义中包括汽车的品牌、颜色、最高时速这些属性,如图 1 5所示。每个对象的这些属性都有自己的值。所有的由类定义建立的对象都共享类的方法。但是它们都拥有在类方法中定义的所有变量的副本(copy)。
2. 方法
对象的操作由Delphi的方法来指定。要使一个对象做某件事情,就要调用它的相应方法。在用户程序中,这由一行给出了方法名及参数列表的代码来完成。假如用户想要改变汽车的颜色。在程序中,可以编写如下代码:

mycar.setColor(yellow);

这行代码包括了对象名myCar;方法名setColor;参数yellow(包含于括号中)。可以看到,一个方法的参数是传送给方法的数值,这些数值在方法执行中被用到。在这个例子中,setColor方法把数值yellow赋给汽车对象的color属性,然后改变汽车的颜色。这个过程也被称为向对象发送一条消息。在本例中,用户向汽车对象发送了一条消息,要求改变颜色属性,并指定了新的颜色值。
有时为了区分到底是一个对象的属性还是方法,就在方法名后加上括号,比如 stop()。当参数传递给方法时,它们在括号中给出。
方法也可以返回一个数据值。因而下面这个方法:

myCar.getColor();

将返回汽车的颜色值,在本例中为黄色。

图 1 5拥有属性和方法的类和对象
1.2.2 封装
封装(encapsulation)是一个面向对象的术语。它的意思很简单,就是把东西包装起来。换言之,属性定义和方法都包装于类定义之中,类定义可以看成是封装构成类的属性和方法。通过限定类成员的可见性,可以使得类成员中的某些属性和方法能够不被程序的其他部分看见,它们“隐藏”了起来,它们只能在所定义的类中使用。从表面上看,这是一个非常简单的概念。但它在被公认为是一个好主意之前,已经花去了30多年的程序设计实践和思考。
在封装出现以前,程序的任何部分都能存取其他的任何部分,这使得改动变得十分困难,因为程序员永远不知道这种改动会产生什么样的影响。当发现一个变量拥有一个糟糕的值时,用户永远也不知道如何变成这样的情况。封装简化了编程和维护,因为程序改动的副作用往往被局限于程序中一个小的区域内,极大地缩小了潜在问题的影响范围。所以封装的确是个好主意。它只是看起来简单,但直到现在我们才懂得它的妙处。
如果用户习惯于组合不同的数据类来创建复合数据结构,那么可以把类定义看作是建立在类合成之上的数据结构,因为它封装的不仅是数据定义,而且是操纵这些数据的方法。
封装的根本目的是保证对象的属性只能通过对象的方法进行存取。这种实现需要额外的编码,但它保证了任何使用该对象的编码都独立于该对象执行的实现细节。这使得用户可以按个人意愿改变对象的执行过程。牢记这样一点,只要对象的程序设计接口不变,也就是对象方法的结构不变,那么任何使用该对象的代码都能像以前一样正常地工作。
1.2.3 继承
图 1 6显示了面向对象编程的另一重要方面——继承性。注意类Vehicle定义了属性make、color和topSpeed。当定义派生类Car时,它自动继承基类Vehicle中的属性和方法。类Car称为一个派生类或子类。类Vehicle称为Car的基类。

图 1 6Vehicle类是Truck、Motorcycle、Bicycle和Car的基类
继承节省了在定义新类中的大量工作,因为编程者可以方便地重用代码。比如,当创建派生类时,Car.color和Car.topSpeed属性被自动地定义,引用方法Car.start()时会自动调用在类Vehicle中定义的方法start()。
但一个派生类不必非得使用继承下来的属性和方法。一个派生类可以选择覆盖(override)已有的属性和方法,或添加新的属性和方法。用户也可添加其他的属性和方法来满足具体的需要——比如说摩托的撑脚架。
只有当用户想向自己新类的定义中添加新的操作,或者把已存在类的缺省行为融合进自己的新类中时,才需要继承一个已存在的类定义。在Delphi中,这称为派生一个类。要添加新的方法和属性,只需定义它们即可。要覆盖一个已存在的方法,就要在派生类中(Car)定义一个新方法,该方法与基类中(Vehicle)被覆盖的方法有相同的名字和参数列表。
如果派生类没有覆盖stop()方法,那么基类Vehicle中的缺省stop()方法就被调用。该方法可能关掉发动机。
1.2.4 多态性
这种同样的方法名因为调用的方式不同而执行完全不同的例程的能力带来了面向对象编程中另一个强大的方面——多态性(polymorphism)。“Poly”的意思是“许多”,“morph"意为“形状”。所以“polymorphism”从词义上说就是许多形态的意义。它在实际中的含义就是不同的对象有相同的一般的轮廓或形态,但具体执行的过程却大相径庭。
多态性使用户可以写更加通用的过程。用户可以编写一个过程来控制Vehicle对象,说“当看到一个停车标志时,执行Stop()方法”,这样的通用Stop()方法与驾驶什么车无关。用户可以让派生类去操心如何完成stop()方法,而继续在更高的抽象级别上编写自己的通用过程。比如说,即使Car对象的stop()方法与Truck对象的stop()方法完全不一样(卡车在18个档之间来回切换),用户也可以编写Vehicle.stop(),并知道哪一个合适的方法将被调用。
多态性也使得用户在以后不费多大力气就可以派生程序。假设用户在为小汽车和卡车构建应用程序。用户知道还存在摩托车和自行车,但在当时并不太在意它们。用户可以写一个处理Vehicle类的程序,把Truck和Car派生类定义为Vehicle的特例。以后,如果需要,无需费劲就可派生。通过Vehicle类来定义Motorcycle和Bicycle类,并相信为Vehicle类编写的所有代码都可以为这些新类工作。
方法重载(overload)也可以提供类似于多态性的好处。方法重载意味着两个方法有相同的名字但参数不同。方法的名字与它参数的个数及类型决定了该方法的特征(signature)。一个类可以拥有多个同名的方法,只要每个方法都有不同的特征即可。
注意:返回值的数据类型不参与决定的方法的特征。
与多态性一样,方法重载赋予了用户某种程度的概括能力。使用不同的方法特征使用户可以指定同名但细节不同的多种操作。比如,Car类拥有一个标准的stop()方法,即脚踏刹车闸。它也可以有一定义为“紧急刹车”,即双脚猛踩刹车闸的stop(emergency)方法,和定义为“冰面刹车”,即轻微地踩刹车闸但有一滑动就松开刹车闸的stop(ice)方法。使用相同的名字和不同的参数来调用不同的方法使用户不必为同一类操作的不同变体而绞尽脑汁取新的名字。这也使类的使用者可以更容易地记住方法的名称。
注意:重载和多态的机制有本质的区别。重载不同于覆盖,准确地讲,它不是面向对象专有的。在后面第5章“深入多态”的“重载和覆盖”一节我会做详细的讨论。
 
有意思!有收益!
搬凳子,等高手来讲课!
 
newdream:你是刘艺?
 
newdream:不妨到这里谈谈http://www.delphibbs.com/delphibbs/dispq.asp?lid=1951482
kk2000楼主别见怪,我们的要想法都要提高
 
newdream:如果你的书里面有这样详细的说明!我肯定会买了。非常感谢!
活化石:我怎么会见怪呢!不管是谁来我都欢迎。
谢谢各位的指导!
 
http://www.wushuang.net/article/insided.htm 左大侠的文章,入门最适宜
 
接口也是和面向对象密切相关的一个东西,
可以在不用继承的条件下实现对象的多态性
 
To LiChaoHui:
接口的使用更有弹性,但接口也有继承呀!第一,接口可以继承接口,第二,接口由类来实现,实现类必然要继承该接口。所以用接口实现多态离不开继承。没有继承就没有多态。下面摘录《Delphi面相对象编程思想》中“接口和面相对象编程”一节。

=================================

接口语言可以看作包含抽象函数的最终抽象类。甚至从句法上讲,接口与只包含抽象函数的抽象类相似。
一个类可以实现一个接口,如此一来,类就要实现接口的抽象函数成员,与派生类保证要实现它的基类的抽象函数一样。所以接口是通过动态绑定函数调用抽象基类的替代方式。例如前面示例程序 6 1和示例程序 6 2中,我们可以用一个等价的IGreetable接口来替换TMan类,此接口也包含一个抽象的SayHello方法。TChinese、TAmerican、TFrench等派生类也将实现IGreetable接口。这不会影响动态绑定调用SayHello函数的能力。不过这个例子还不能演示和说明接口的威力和妙用。
即使接口和抽象类从句法和语义上密切相关,他们仍有一个重要的区别:接口只能包含抽象方法,而抽象类除了包含抽象方法外,还可以包含其他数据成员和非抽象方法。另外,一个类最多只有一个基类,但可以有多个接口。
一个接口可以自身实现一个或多个接口,从而继承这些接口的方法,形成与类层次一样的接口层次。
在前面的内容中,普通类形成了健全的分类类层次,其中每一对基类和派生类表示一个“is-a”关系。这些层次提供了极大的好处,但也从一定程度上限制了多态的使用。原因是:在一个类层次中要充分利用多态,必须具备一组相同的祖先类。此祖先类声明了接口(通常以抽象方法形式)以及派生类中单个实现过程间的通信协议。如果我们想要划分成组的类不具有相同的祖先类怎么办呢?如果他们分布于整个程序的多个地方,甚至位于多个单独的类层次中,又该如何处理?

图 6 11接口让3个无关的类实现相同的抽象方法
假如我们重新考虑“来自世界的问候”的那个例子,将类层次由一个TMan增加到TUnkown、TMachine等数个,如图 6 11所示。其中,我们希望机器人和外星人都能像人类一样进行问候,都有自己的SayHello方法,尽管可能问候的方式不同。但我们无法将它们归纳到一个共同的祖先类。如果硬要用一个抽象类放置到TMan、TUnkown和TMachine的上层,并包含一个抽象方法SayHello的话,虽然可以工作,但却扰乱了类的层次关系,最终导致设计概念上的不伦不类关系。
最理想的解决方式是创建一个接口,取名为IGreetable,用它来包含抽象方法SayHello,并让3个类实现这一接口,如图 6 11所示。该接口声明如下:

IGreetable= interface
['{D91DDE09-0FC4-4FE9-AE0D-9877E2F73BF6}']
function SayHello:string;
end;

在前面“来自世界的问候”的那个例子中,我们已经初步体验到多态的作用。
在示例程序 6 7中,TMan、TET和TRobot三个没有血缘关系的类都继承自IGreetable,他们实现SayHello方法的内容也不一样。有意思的是在TfrmSayHello中他们仍然可以用一个sayhello(greeting:IGreetable)来实现多态,如示例程序 6 7和图 6 12所示。
示例程序 6 7 扩充新类TET和TRobot后的uSayHello
unit uSayHello;

interface

type

IGreetable= interface
['{D91DDE09-0FC4-4FE9-AE0D-9877E2F73BF6}']
function SayHello:pChar;
end;

TMan = class (TInterfacedObject,IGreetable)
public
Language: string;
Married: Boolean;
Name: string;
SkinColor: string;
constructor create; virtual;
function SayHello:pChar;virtual;abstract;
end;

TChinese = class (TMan)
public
constructor create;override;
private
function SayHello:pChar;override;
end;

TAmerican = class (TMan)
public
constructor create;override;
private
function SayHello:pChar;override;
end;

TFrench = class (TMan)
public
constructor create;override;
private
function SayHello:pChar;override;
end;

TKorean = class (TMan)
public
constructor create;override;
private
function SayHello:pChar;override;
end;

TET = class (TInterfacedObject,IGreetable)
private
function SayHello:pChar;
end;

TRobot = class (TInterfacedObject,IGreetable)
private
function SayHello:pChar;
end;

implementation

constructor TMan.create;
begin
Name:='张三';
Language:='中文';
SkinColor:='黄色';
end;

constructor TChinese.create;
begin
inherited;
end;

constructor TAmerican.create;
begin
Name:='Lee';
Language:='英文';
SkinColor:='黑色';
end;

constructor TFrench.create;
begin
Name:='苏菲';
Language:='法文';
SkinColor:='白色';
end;

constructor TKorean.create;
begin
Name:='金知中';
Language:='韩文';
SkinColor:='黄色';
end;

function TChinese.SayHello;
begin
Result:='chinese.bmp';
end;

function TAmerican.SayHello;
begin
Result:='American.bmp';
end;

function TFrench.SayHello;
begin
Result:='French.bmp';
end;

function TKorean.SayHello;
begin
Result:='Korean.bmp';
end;

function TET.SayHello;
begin
Result:='ET.bmp';;
end;

function TRobot.SayHello;
begin
Result:='Robot.bmp';;
end;

end.
示例程序 6 8扩充新类TET和TRobot后的ufrmSayHello
unit ufrmSayHello;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls,uSayHello;

type
TfrmSayHello = class(TForm)
btnUSA: TButton;
btnKorean: TButton;
btnCN: TButton;
btnFrench: TButton;
Image1: TImage;
btnET: TButton;
btnRobot: TButton;
procedure btnUSAClick(Sender: TObject);
procedure btnCNClick(Sender: TObject);
procedure btnFrenchClick(Sender: TObject);
procedure btnKoreanClick(Sender: TObject);
procedure btnETClick(Sender: TObject);
procedure btnRobotClick(Sender: TObject);
private
procedure sayhello(greeting:IGreetable);
public
{ Public declarations }
end;

var
frmSayHello: TfrmSayHello;

implementation



{$R *.dfm}
procedure TfrmSayHello.sayhello(greeting:IGreetable);
begin
image1.Picture.LoadFromFile(greeting.sayhello);
end;

procedure TfrmSayHello.btnUSAClick(Sender: TObject);
begin
sayhello(TAmerican.create);
end;

procedure TfrmSayHello.btnCNClick(Sender: TObject);
begin
sayhello(TChinese.create);
end;

procedure TfrmSayHello.btnFrenchClick(Sender: TObject);
begin
sayhello(TFrench.create);
end;

procedure TfrmSayHello.btnKoreanClick(Sender: TObject);
begin
sayhello(TKorean.create);
end;

procedure TfrmSayHello.btnETClick(Sender: TObject);
begin
sayhello(TET.create);
end;

procedure TfrmSayHello.btnRobotClick(Sender: TObject);
begin
sayhello(TRobot.create);
end;

end.

图 6 12 使用IGreetable接口后,外星人也能SayHello
从示例程序 6 7中,我们可以看出IGreetable接口的SayHello方法在 TMan、TET和TRobot中实现方法也有区别。TET和TRobot中是直接实现SayHello方法的。而TMan并没有这样做,他只是将SayHello方法作为抽象方法留给派生类去实现。
请注意,继承关系常常描述成“is-a” (是一个)关系,例如,TChinese“is-a”TMan。在Delphi的is运算符中可以看到相同的概念,它可以用于测试一个AMan变量是否“is-a”TChinese。但是,在很多复杂的情况下,简单的“is-a”关系会被打破。比如,中国人“是一个”人,但机器人并不“是一个”人,这并不意味着机器人不能用自己的方式向你问候。可我们决不会让机器人从TMan继承下来。类继承迫使派生类存储所有在基类中声明了的数据成员,在这种情况下,会导致派生类并不需要那样的信息,比如:机器人不会需要肤色(没有皮肤的金属机器人)。尽管如此,类继承是代码重用的一个有效工具。派生类能轻易地继承基类的数据成员、方法和属性,从而避免重复实现普通的方法。比如,TChinese 需要继承TMan 的Language、Married、Name、SkinColor数据成员和create、SayHello方法。
通过面向对象的分析,我们可以将中国人和机器人泛化成能问候(something greetable)的某种类型对象,并抽象出IGreetable接口。这样使用接口还有一个最大的好处就是将类型继承与类继承分离开来。在一个强类型化的语言,如Delphi中,编译器将类作为类型对待,因此类继承变成与类型继承了。但是我们要明白:类是一种类型,类的继承和类型的继承并不完全一样。比如,接口的继承就是一种类型的继承,他和类的继承决不等同。前面我们就是将类型继承(中国人和机器人都是一种能问候的类型)与类继承(类TChinese继承类TMan的数据成员和方法)分离开来。这就是说,在类型继承时使用接口,以提高程序可扩展性;而类继承仅在需要用到的地方使用,继承了的数据成员和方法可提高程序的可重用性。下面举例说明。
为了进一步演示类继承和类型继承的差别和类型继承与类继承分离的好处,我们修改一下前面“来自世界的问候”的那个例子如下:
示例程序 6 9 进一步修改的uSayHello
unit uSayHello;

interface

type

IGreetable= interface
['{D91DDE09-0FC4-4FE9-AE0D-9877E2F73BF6}']
function SayHello:pChar;
end;

TMan = class (TInterfacedObject,IGreetable)
public
Language: string;
Married: Boolean;
Name: string;
SkinColor: string;
constructor create; virtual;
function SayHello:pChar;virtual;abstract;
end;

TChinese = class (TMan)
public
constructor create;override;
private
function SayHello:pChar;override;
end;

TAmerican = class (TMan)
public
constructor create;override;
private
function SayHello:pChar;override;
end;

TFrench = class (TMan)
public
constructor create;override;
private
function SayHello:pChar;override;
end;

TKorean = class (TMan)
public
constructor create;override;
private
function SayHello:pChar;override;
end;

TET = class (TInterfacedObject,IGreetable)
private
function SayHello:pChar;
end;

TRobot = class (TInterfacedObject,IGreetable)
private
function SayHello:pChar;
end;

implementation

constructor TMan.create;
begin
Name:='张三';
Language:='中文';
SkinColor:='黄色';
end;

constructor TChinese.create;
begin
inherited;
end;

constructor TAmerican.create;
begin
Name:='Lee';
Language:='英文';
SkinColor:='黑色';
end;

constructor TFrench.create;
begin
Name:='苏菲';
Language:='法文';
SkinColor:='白色';
end;

constructor TKorean.create;
begin
Name:='金知中';
Language:='韩文';
SkinColor:='黄色';
end;

function TChinese.SayHello;
begin
Result:='chinese.bmp';
end;

function TAmerican.SayHello;
begin
Result:='American.bmp';
end;

function TFrench.SayHello;
begin
Result:='French.bmp';
end;

function TKorean.SayHello;
begin
Result:='Korean.bmp';
end;

function TET.SayHello;
begin
Result:='外星人#$%^&*(@@)';
end;

function TRobot.SayHello;
begin
Result:='机器人0111000011110000111111';
end;

end.

示例程序 6 10进一步修改的ufrmSayHello
unit ufrmSayHello;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls,uSayHello;

type
TfrmSayHello = class(TForm)
GroupBox1: TGroupBox;
edtName: TLabeledEdit;
edtSkinColor: TLabeledEdit;
edtLanguage: TLabeledEdit;
btnUSA: TButton;
btnKorean: TButton;
btnCN: TButton;
btnFrench: TButton;
Image1: TImage;
btnET: TButton;
btnRobot: TButton;
procedure btnUSAClick(Sender: TObject);
procedure btnCNClick(Sender: TObject);
procedure btnFrenchClick(Sender: TObject);
procedure btnKoreanClick(Sender: TObject);
procedure btnETClick(Sender: TObject);
procedure btnRobotClick(Sender: TObject);
private
procedure sayhello(AMan:TMan);overload;
procedure sayhello(greeting:IGreetable);overload;
public
{ Public declarations }
end;

var
frmSayHello: TfrmSayHello;

implementation

{$R *.dfm}

//这里的实现方法和原来(示例程序 5 6)的一样,没有改动
procedure TfrmSayHello.sayhello(AMan:TMan);
begin
edtName.Text:=AMan.Name;
edtLanguage.Text:=AMan.Language;
edtSkinColor.Text:=AMan.SkinColor;
image1.Picture.LoadFromFile(AMan.sayHello);
end;

//这里新增来自非人类的另类问候实现方法。
//和示例程序 5 6比较,这是唯一的改动。
//通过方法重载,使程序的改动降低到最少。
procedure TfrmSayHello.sayhello(greeting:IGreetable);
begin
edtName.Text:=copy(greeting.sayhello,1,6);
edtLanguage.Text:=copy(greeting.sayhello,7,4);
edtSkinColor.Text:=copy(greeting.sayhello,11,6);
application.MessageBox(greeting.sayhello,
'问候',MB_ICONINFORMATION+MB_OK);
end;

procedure TfrmSayHello.btnUSAClick(Sender: TObject);
begin
sayhello(TAmerican.create);
end;

procedure TfrmSayHello.btnCNClick(Sender: TObject);
begin
sayhello(TChinese.create);
end;

procedure TfrmSayHello.btnFrenchClick(Sender: TObject);
begin
sayhello(TFrench.create);
end;

procedure TfrmSayHello.btnKoreanClick(Sender: TObject);
begin
sayhello(TKorean.create);
end;

procedure TfrmSayHello.btnETClick(Sender: TObject);
begin
sayhello(TET.create);
end;

procedure TfrmSayHello.btnRobotClick(Sender: TObject);
begin
sayhello(TRobot.create);
end;

end.

大家注意到我在示例程序 6 10中分别重载了sayhello方法,于是可以在TfrmSayHello(实际上是界面类,通常是位于表示层中)中将人类的和非人类的sayhello方法分别实现,不过实现的代码完全不一样。

procedure sayhello(AMan:TMan);overload;
procedure sayhello(greeting:IGreetable);overload;

虽然按钮单击事件中的写法都是Txxx.create,但是作为TMan的派生类都能通过重载找到适合自己的sayhello(AMan:TMan)方法,并实现自己的显示内容;TET和TRobot也能通过重载找到适合自己的sayhello(greeting:IGreetable)方法,实现与TMan的派生类完全不同的显示内容。值得注意的是,示例程序 6 10中的TfrmSayHello.sayhello方法已经不仅仅像示例程序 6 8中那样仅有一条代码:

image1.Picture.LoadFromFile(greeting.sayhello);

这意味着类型继承与类继承分离后,我们可以享受到鱼和熊掌兼得的好处:既通过接口实现多态的灵活性,又通过继承获得代码的重用性。实际上运行该程序,我们也能够立即观察到它既保留了原有的功能(来自世界各国的问候),又扩展了新的功能(来自非人类的另类问候)。
这个例子巧妙地用到了覆盖、重载、继承、多态、抽象方法和抽象类、虚方法、接口、类的类型转换(类的向上转型,类向继承的接口转型)等众多概念,如果能够深入理解掌握,必将会使你在面向对象编程方面功力大增,值得读者好好研习和体会。
---------------
我的个人网站:http://www.liu-yi.net
 
呵呵,又来了个超级高手!
 
高手就是高手,真是不同反响啊,前言都让我看的一晕一晕的
杜甫说,汝果欲作诗,功夫在诗外,看来编程也是一样啊
研究了中国古典学说后编程能力会有一个质的飞跃啊,
 
DFW真是热闹
[:D]
 
向高手学习。!
 
好好听讲呀!
 
我听下来感觉象生小孩啊
 
顶部