求实例啊.............delphi得面向对象得实例小程序啊(100分)

  • 主题发起人 主题发起人 paulcross
  • 开始时间 开始时间
P

paulcross

Unregistered / Unconfirmed
GUEST, unregistred user!
求delphi面向对象编程的 小实例啊 要有功能得 不要那种 动物--鱼,狗 之类的
要有自己写的类 接口 方法 属性等等 别太大太乱了就好

亏这里还是delphi最大的论坛 怎么就没有人会面向对象呢
[:(!]
 
我都是以面向对象的写法来着。
刚刚接触的Delphi的朋友,可能最感兴趣的就是它丰富、强大的VCL(可视化构件
库)。仅仅向窗体上扔几个构件,甚至不用动手写代码,就能很容易地做出一个有实用
价值的程序,真是令人激动。但是,VCL只是Delphi的一小部分,Delphi的优秀远远不
只是表现在V
CL上。如果你仅仅停留在使用VCL这一阶段,那么你永远也不可能成为一个真正的
Delphi高手。记住,必须超越VCL,才能够接触到Delphi的核心。
  那么,在Delphi的VCL后面,到底隐藏着什么呢?本文将讨论两个比较高级的
Delphi主题:OOP和数据库编程。
  本文假定读者已经具有Delphi编程的基础知识,例如,熟悉Pascal语言的一般语
法,掌握简单的VCL使用,会使用Data-Ware构件编写基本的数据库程序,等等。本文不
会重复VCL的使用这样的问题。
  
  1、OOP
  
  OOP的英文全称是Object Oriented Programming,翻译过来就是面向对象编程。
OOP是一种非常重要的编程思想。也许你会对这种抽象的东西不感兴趣,可是几乎任何
一个高手都会告诉你:“语言并不重要,重要的是编程思想。”
  大家知道,Delphi的语言基础是Object Pascal。这是Borland在传统的Pascal语言
中增加了面向对象的特性后发展而成,并且特地冠以Object的字样,以示与传统的
Pascal语言的差别,可见面向对象技术对其影响之大。可以说,Delphi构建在Object
Pascal的基础之上,而Object Pascal构建在面向对象技术之上。
  事实上,不但Delphi,OOP也是C++、Java等其他许多现代编程语言的基础(Visual
Basic不完全地支持OOP)。熟练地掌握OOP技术,是深入掌握Delphi的必要条件,是迈
入高手境界的必经之路,也是一个成熟的程序员的标志之一。理解了OOP技术之后,很
多以前令你困惑的东西会迎刃而解。
  有趣的是,虽然Delphi是完全基于OOP的,但是一个完全不了解OOP的程序员也能够
使用Delphi编写程序,因为Delphi会自动完成绝大多数的工作。当你开始学习Delphi的
时候,你可能无法想象,当简单地往窗体上添加一个按钮时,Delphi会完成多么复杂的
工作吧
!但是既然有志于深入Delphi的世界,成为一个真正的程序员,我们就应该对Delphi的
细节具有好奇心。
  这些理论可能会让人觉得枯燥和望而生畏,其实当你掌握了它之后就会觉得没什么
了。当然,你需要有足够的毅力。

  OOP有三个主要的特征:
 
  1.1 数据封装
  
  让我们先看一段代码:
  
  type
   TDate = class
   Mouth,day,Year:Integer;
   procedure SetValue(m,d,y:Integer);
   function LeapYear:Boolean;
  end;
  
  我们首先会看到class关键字,它的中文译名为“类”。类是一个非常重要的概
念。根据权威的定义,类是:一种用户定义的数据类型,它具有自己的说明和一些操
作。一个类中含有一些内部数据和一些过程或函数形式的对象方法,通常来描述一些非
常相似的对象
所具有的共同特征和行为。
  这个定义可能比较晦涩。你可以把类想象为一种特殊的Record类型,其中不但可能
包含数据,而且可能包含函数和过程(在OOP中称之为方法)。这些数据和方法被统称
为类的成员。
  上面这个类很显然是一个日期类型,它包括Mouth,Day,Year这三个数据成员,和
SetValue、LeapYear这两个方法。顺便说一句,在Delphi中,习惯以字母T作为每个类
的前缀,就象Viusal C++中习惯以字母C作为每个类的前缀一样。
  Mouth,Day,Year这三个数据成员指定了该日期的年、月、日。SetValue方法为这
三个数据成员赋值,而LeapYear检查当前日期所在的那一年是否是闰年。下面我们给出
这两个方法的实现部分:
  
  procedure TDate.SetValue(m,d,y):Integer;
  begin
   Mouth := m;
   Day := d;
   Year := y;
  end;
  
  function TDate.LeapYear:Boolean;
  begin
   if (Year mod 4 <> 0) then
    LeapYear := False
   else if (Year mod 100 <> 0)
    LeapYear := True
   else if (Year mod 400 <> 0)
    LeapYear := False
   else
    LeapYear := True;
  end;
  
  实现了这些定义之后,就可以这样调用它们:
  
  var
   ADay: TDate;
  begin
   //建立一个对象
   ADay := TDate.create;
   //使用之
   ADay.SetValue(1,1,2000);
   if ADay.LeapYear then
    ShowMessage('闰年:' + Inttostr(ADay.year));
   //释放对象
   ADay.free;
  end;
  
  我们来逐行解释这些代码的含义。var后面那一行声明了一个TDate类的变量。
  声明了变量之后,我们怎么使用它呢?使用TDate类的Create方法可以建立一个该
类的对象,并将其赋予ADay变量。
  现在我们又接触到了OOP中另一个重要的概念:对象。什么是对象?简言之,对象
就是类的实例,或者说,是类定义的数据类型的变量。当建立一个类的对象时,系统为
它分配一块内存。例如我们定义一个变量A为Integer类型,那么,Integer是一个数据
类型,A就是一个实例。类与对象的关系就类似于这两者之间的关系。区别类和对象是
非常重要的,甚至一些专业的程序员都往往将他们搞混。
  细心的读者可能注意到,在TDate类的定义中,并没有Create这个方法。那么这个
Create方法是从哪里来的呢?Create方法是每一个Class都具有隐含的方法,它的作用
是建立这个类的实例。请注意,在这里,类和其他的数据类型是不同的。其他的数据类
型都是声明了变量之后就可以直接使用,而类类型必须在使用Create方法创建它的实例
(对象)之后才能使用。
  事实上,在C++和其他大多数的OOP语言中,声明一个类的变量就能够同时建立起这
个类的对象。而Delphi(包括它的孪生兄弟C++Builder)在这方面与众不同,必须要
Create一下才能真正建立对象。同时,在这个对象不再需要时,必须要手工调用free方
法释放这个对象(当然,free方法也是每个类隐含的)。这和Delphi独特的“对象引用
模型”有关,有兴趣的朋友可以查阅有关资料,我就不多说了。
  这种情况造成了一个非常有趣的现象,那就是,编程的初学者往往忘记在使用对象
之前create它,从而出错,但从C++转向Delphi的高手也常常犯同样的错误……
  顺便告诉大家一个诀窍,当编译器出现“Read of Address: ffffffff”这样的错
误时,多半是因为在使用对象之前忘了Create,可以从这方面入手检查代码。另外,也
千万不要忘记在不需要它时使用free释放掉,否则可能造成内存泄漏。
  在建立和释放对象的代码的中间,是使用对象的代码。访问对象的数据成员非常简
单,和Record类型没有什么区别。可以点号表达式来访问它们:
  
  ADay.Year := 2000;
  ADay.Mouth := 1;
  ADay.Day := 1;
  
  同样,也可以使用点号表达式来调用对象的方法。如果你阅读了方法实现部分的代
码,你可以很容易地发现,ADay.SetValue1,1,2000)这一句分别为三个数据成员赋了
值,而ADay.LeapYear调用则返回当前日期所在年是否为闰年。至此,整段代码的意义
也就清楚了。
  然而,类不仅仅这么简单。上面这个例子是一个非常简单的类,可以直接访问它的
任何成员(数据和方法)。但某些类的成员是不能被随便访问的。Delphi中用三个关键
字区分这些成员的访问权限:
  
  表1

Private 该类型的成员只能在声明类中被访问
Public 该类型的成员可以被程序中的任何地方的代码访问
Protected 该类型的成员只能在声明类以及声明类的派生类中被访问
  
  Protected类型的成员以及什么是“派生类”等问题我们留到以后再进行讨论,现
在我们将注意力集中在前两者。
  Public类型就是在上面例子中的那种类型,这个很好理解。而Private类型,根据
表格中的简单解释,只能在该成员被声明的那个类(也就是该成员所属的那个类啦)中
被访问,越出这个界限,它就是不可见的。那么,Private类型的成员将如何被使用呢
?简单地说,就是通过一个Public类的方法来访问它。
  让我们看一个新的例子:
  
  type
   TDate = class
   private
   Mouth,day,Year:Integer;
   Public
   procedure SetValue(m,d,y:Integer);
   function LeapYear:Boolean;
   function GetText:String;
  end;
  
   在这个类中,Mouth,Day,Year这三个成员被声明为Private成员,因此它们在
类以外的其它地方是不可访问的。也就是说,如果你使用
  
  ADay.Year := 2000;
  
  这样的代码,那么编译器将会报错。但是,我们可以照样通过SetValue方法为它们
赋值:
  
  ADay.SetValue(1,1,2000);
  
  这行代码是合法的,因为SetValue本身是TDate类的成员,而且它又是一个Public
成员。而使用GetText方法则可以得到当前日期值(这也是得到当期日期值的唯一办
法)。
  这样的设置使得类的一些成员被隐含起来,用户只能用一些专门的方法来使用它
们。那些可以被外部代码访问的成员称之为类的接口。这样做有什么好处呢?首先,这
让类的作者可以检测被赋值的内容。比如,用户可能给一个对象赋予13月40日这样的无
效日期。而在隐含了一些成员之后,类的作者可以在方法的代码中检测这些值是否有
效,从而大大地减少了产生错误的机会。其次,使用规范的类,作者可以随时修改类内
部的代码,而使用该类的代码却无需任何修改!这样使得代码的维护成了一件轻松的事
件,特别是对于多人协作的大型软件而言。
  这就叫做数据的封装(encapsulation)。这是OOP的第一个特征。一个优秀的OOP
程序员,应该在设计类的时候,就确定将哪些重要的数据封装起来,并给出一个高效率
的接口。
  需要指出的一点是,表1中Private部分的论述对于“标准的”OOP语言(例如C++)
是完全正确的,但对于Delphi有一个例外。在Delphi中,Private成员除了在声明类中
可以访问外,在声明类所在的单元(.pas文件)中的任何地方都能被访问,不论这些代
码与声明
类的关系如何。严格来说,这是违反OOP的原则的,我不明白Borland为何要这么做(据
说是为了方便)。在关于Delphi的优劣性的讨论中,这是常被涉及的一个问题。
  
  1.2 继承与派生
  
  我们再来看一段代码:
  
  type
    TNewDate = class(TDate)
   Public
    function GetTextNew:String;
  end;
  
  function GetText:String;
  begin
   return := inttostr(Mouth) + ':' + inttostr(Day) + ':' +
inttostr(Year);
  end;
  
  可以看到,在class后面出现一个包含在括号中的类名。这种语法表示新的类继承
了一个旧的类。继承了原有类的类称之为派生类,也叫子类,被继承的类称之为基类,
也叫父类。
  派生类与基类之间是什么关系呢?当派生类继承自一个基类时,它自动具有基类的
所有数据、方法以及其他类型,无须在派生类中再做说明。例如,可以象下面这段代码
这样使用TNewDate类:
  
  var
   ADay: TNewDate;
  begin
   ADay := TNewDate.create;
   ADay.SetValue(1,1,2000);
   if ADay.LeapYear then
    ShowMessage('闰年:' + Inttostr(ADay.year));
   ADay.free;
  end;
  
  而且,派生类还可以在基类的基础上加入自己的数据和方法。可以看到在TnewDate
类中增加了一个新的方法GetTextNew。下面给出这个方法的实现部分:
  
  function GetTextNew:String;
  begin
   return := GetText;
  end;
  
  然后调用它:
  
  ADay.GetTextNew;
  
  这个新的方法工作得很好。
  为什么GetTextNew方法必须调用基类中的GetText方法,而不能直接使用GetText方
法中的那些代码呢?原因是,Mouth,Day,Year这三个成员被声明为Private成员,因
此它们即使在派生类中也是不能被访问的,所以必须调用基类中的GetText方法,间接
地使用它们。如果要直接使用它们的话,可以将这三个成员的属性从Private改为
Protected。在表1中可以看到,Protected属性的成员可以在声明类以及声明类的派生
类中被访问,然而仍然不能被这两种情况以外的其他代码所访问。现在我们终于可以理
解了,这个特殊的属性实际上提供了极大的方便:它使得类的成员被封装,避免了混
乱,同时又能够让派生类方便地使用它们。
  (如果你是一个细心的人,你可能发现上面的话中间有一个小小的仳漏。当你真的
在GetTextNew方法中访问了基类的Private成员的话,你可能会惊奇地发现程序也能够
编译通过而且正常运行!其实,这个问题和OOP本身没有关系。上面我已经说过,在
Delphi中,Private成员在声明类所在的单元文件中的任何地方都能被访问,因此如果
TNewDate类和TDate类在同一个.pas文件中时,这种情况就不足为怪了。)
  怎么样,是不是觉得非常奇妙?通过这种继承的机制,类不再仅仅是数据和方法的
封装,它提供了开放性。你可以方便地继承一个功能强大的类,然后添加进自己需要的
特性,同时,你又不需要对基类进行任何的修改。相反,原作者对基类的任何改动,都
可以在你的新类中立即反映出来。这非常符合代码的重用要求。
  这种继承机制也非常符合现实世界中的情形。可以设想,一般意义上的“动物”是
一个类,具有自己的一些特征(成员);而“狗”是“动物”的派生类,它具有动物的
所有特征,同时还具有自己独有的特征(四条腿,汪汪叫,等等)。而“狗”这个类可
以继续派生下去,例如“黑狗”“白狗”,它们除了保留狗的全部特征之外,还具有自
己的特征(黑颜色,白颜色,等等)。而具体到一只活生生的,可以认为它就是“黑
狗”或“白狗”(或其他什么狗)的一个实例(对象)。
  OOP这种对现实世界的模拟不仅极大地简化了代码的维护,而且使得整个编程思想
产生了革命性的变化,较之模块化编程有了飞跃的进步。
  如果你曾经仔细阅读过VCL的资料甚至它的源代码,你就可以发现,整个VCL都是建
立在这种强大的封装-继承的机制之上的。你可以看到一张详细的VCL层次结构图,就象
是一个庞大的家谱,各种VCL构件通过层层继承而产生。例如,一个简简单单的TForm
类,就是许多次继承之后的产物:
  TObject - TPersistent - TConponent - TControl - TWinControl -
TScrollingWinControl - TCustomForm - TForm
  不但Delphi的VCL,Visual C++中的著名的MFC(Microsoft Foundation Class,微
软基本类库),以及以前Borland C++中风光一时的OWL(Object Window Library,对
象窗口类库),都是建立在这种机制之上。所不同的是,对于前两种语言,你要花上好
几个月的功夫去基本掌握那些繁复无比的类,才能写出比较有实用价值的程序,而在
Delphi中,大部分的工作Delphi都已经自动帮你完成了。例如,每次你向程序中加入一
个窗体时,Delphi就自动为你从TForm派生一个新类(默认为TForm1),并且为这个新
类创造一个实例。你对这个窗体的改动(添加构件和代码之类),无非是为这个派生类
加入一些新的特性而已;你再也用不着自己去处理最大化、最小化、改变大小这一类的
情况,因为这些代码都在基类中被实现,而被派生类所继承了。这就是Delphi的伟大之
处。当然,Delphi的VCL也绝不比MFC或OWL逊色(事实上它是由后者演变而来)。
  (可能有人会问起VB的情况。VB不支持继承,因此并没有什么复杂的类库,它自己
的控件也少得可怜,主要是使用ActiveX控件。)。
  也许你已经若有所悟,为你的发现而心痒难骚了吧。但是,我们要讨论的东西当然
不会仅仅这么简单。
  在1.1部分(“数据封装”),我们讲到了“Create方法是每一个Class都具有隐含
的方法”。其实,这种说法是不准确的。事实是,在Delphi中,所有的类都默认继承自
一个最基础的类TOject,甚至在你并未指定继承的类名也是如此。Create方法是
TObject类具有的方法,因此理所当然,所有的类都自动获得了Create方法,不管你是
否实现过它。想想看就知道了:如果没有Create方法的话,怎样建立一个对象呢?
  你可能注意到了Create方法是一个特殊的方法。不错,Create方法的确非常特殊,
甚至于它的“头衔”不再是function或procedure,而是Constructor(构造器)。你可
以在VCL的源码中见到这样一些例子:
  
  Constructor Create;
  
  构造器不仅是一个Delphi关键字,而且是一个OOP方法学的名词。与之相对应的,
还有Destructor(毁坏器)。前者负责完成创建一个对象的工作,为它分配内存,后者
负责释放这个对象,回收它的内存。要注意的一点是,Constructor的名字一般是
Create,但Destructor的名字却不是Free,而是Destroy。例如:
  
  Destructor Destroy;
  
  那么,在以前的代码,为什么又使用Free来释放对象呢?二者的区别是,Destroy
会直接释放对象,而Free会事实检查该对象是否存在,如果对象存在,或者对象不为
nil,它才会调用Destroy。因此,程序中应该尽量使用free来释放对象,这样更加安全
一些。(但要
注意,free也不会自动将对象置为nil,所以在调用free之后,最好是再手动将对象置
为nil。)
  象对待一般的函数或过程那样,也可以向构造器传递参数:
  
  type
   TDate = class
    private
    Mouth,day,Year:Integer;
    Public
    function LeapYear:Boolean;
    function GetText:String;
    Constructor Create(m,d,y:Integer);
   end;
  
  procedure TDate.Create(m,d,y):Integer;
   begin
   Mouth := m;
   Day := d;
   Year := y;
  end;
  
  调用它:
  
  ADay: TDate;
  begin
   ADay := TDate.create(1,1,2000);
   if ADay.LeapYear then
    ShowMessage('闰年:' + Inttostr(ADay.year));
   ADay.free;
  end;
  
  这样,在Create方法里就完成了对数据的初始化,而无须再调用SetValue方法了。
  
  接下来,我们将要涉及到另一个重要的、也是很有趣的问题:方法的虚拟与重载。

  可能你已经有点晕了吧……还是先看一个新的例子:
  
  type
   TMyClass = class
    procedure One;virtual;
   end;
  
  type
   TNewClass = class(TMyClass)
    procedure One;override;
   end;
  
  procedure TMyclass.One;virtual;
  begin
   ShowMessage('调用了TMyclass的方法!');
  end;
  
  procedure TNewClass.One; override;
  begin
   Inherited;
   ShowMessage('调用了TNewClass的方法!');
  end;
  
  可以看到,从TMyClass派生了一个新类TNewClass。这两个类都声明了一个名字相
同的方法One。所不同的是,在TMyClass中,One方法后面多了一个Virtual关键字,表
示这个方法是一个虚拟方法(Virtual Method)。而在TNewClass中,One方法后面多了
一个Override关键字,表示该方法进行了重载(Override)。重载技术能够实现许多特
殊的功能。

  让我们来仔细分析它们的实现部分。在TMyclass.One方法的实现部分,调用
ShowMessage过程弹出一个对话框,说明该方法已被调用;这里没有任何特别的地方。
在TNewClass.One方法中,出现了一条以前从未出现过的语句:
  
  Inherited;
  
  这个词的中文意思是“继承”。我们暂时不要去涉及到太过复杂的OOP概念,只要
知道这条语句的功能就是了。它的功能是调用基类中相当的虚拟方法中的代码。例如,
你如果使用以下代码:
  
  var
   AObject: TNewClass;
  begin
   AObject := TNewClass.create;
   AObject.One;
   AObject.free;
  end;
  
  那么程序将弹出两次对话框,第一次是调用TMyclass类中的One方法,第二次才是
TNewClass.One方法中的代码。
  重载技术使得我们不但可以在派生类中添加基类没有的数据和方法,而且可以非常
方便地继承基类中原有方法的代码,只需要简单地加入Inherited就可以了。如果你不
加入Inherited语句,那么基类的相应方法将被新的方法覆盖掉。但是必须注意,重载
只有在基类的方法被标志为Virtual时才能进行,而且重载的方法必须具有和虚拟方法
完全相同的参数类型。
  虚拟方法还有一种特例,即抽象方法:
  
  procedure One;override;abstract;
  
  在One方法后面,不但有override关键字,还多了一个abstract关键字(意为抽
象)。这种方法称为抽象方法(在C++中称为纯虚拟函数)。含有抽象方法的类称为抽
象类。抽象方法的独特之处在于,它只有声明,而根本没有实现部分,如果你企图调用
一个对象的抽象方法,你将得到一个异常。只有当这个类的派生类重载并实现了该方法
之后,它才能够被调用。(在C++中,甚至根本就不能建立一个抽象类的实例。)
  既然如此,那么这种抽象方法又有什么用呢?这个问题我们将在接下来的“多态”
部分进行讨论。
  
  1.3 多态
  
  多态相对来说比较复杂一点。不过不要担心,它的内容比较少,而且如果以前的知
识掌握得比较稳固的话,多态的概念是水到渠成的。
  先来讨论一下类型的兼容性问题。下面是一个例子:
  
  type
   TAnimal = Class
    Procedure Voice;virtual;
    ...
   end;
  
   TDog = Class(TAnimal)
    Procedure Voice;Override;
    ...
   end;
  
  implementation
  
  Procedure TAnimal.Voice;virtual;
  Begin
   PlaySound('Anim.wav',0,snd_Async);
  End;
  
  Procedure TDog.Voice;virtual;
  Begin
   PlaySound('Dog.wav',0,snd_Async);
  End;
  
  TDog类继承了TAnimal类,并重载了其中的Voice方法。PlaySound是一个WIN API函
数,可以播放指定的wav文件。(这个函数的定义在MMSystem.pas文件中可以找到。)
  先看这段代码:
  
  var
   MyAnimal1, MyAnimal2: TAnimal;
  Begin
   MyAnimal1 := TAnimal.Create;
   MyAnimal2 := TDog.Create;
   ...
  
  在实现部分的第一行中,建立了一个TAnimal类型的对象,并将其赋予TAnimal类型
的变量MyAnimal1。这是很正常的事。但在第二行中,建立了一个TDog类型的对象,并
将其赋予了TAnimal类型的变量MyAnimal2。这看上去令人吃惊,但这些代码是完全合法
的。
  众所周知,Pascal以及Object Pascal是一种类型定义严格的语言,你不能将某个
类型的值赋予不同类型的变量,例如将一个整型值赋予布尔型变量,将会导致出错。但
是,这个规则在涉及到OOP领域时,出现了一个重要的例外,那就是:可以将一个子类
的值赋予一个父类类型的变量。但倒过来却是不行的,一个父类的值决不能赋予一个子
类类型的变量。
  如果将这个原则放到现实世界中,那就很容易理解了:“狗”继承自“动物”,因
为狗也是一种动物。所以可以将一个“狗”类型的值赋予“动物”类型的变量,因为
“狗”具有“动物”的一切特征。但反过来,“动物”不具有“狗”的所有特征,因此
反向赋值是不行的。
  那么,这种兼容规则在编程中究竟有什么用处呢?
  请注意下面这段代码:
  
  var
   MyAnimal1, MyAnimal2: TAnimal;
  Begin
   MyAnimal1 := TAnimal.Create;
   MyAnimal2 := TDog.Create;
   MyAnimal1.Sound;
   MyAnimal2.Sound;
   ...
  
  MyAnimal1和MyAnimal2都是TAnimal的变量,而且都调用了Sound方法。但是,执行
的结果是完全不同的:前者执行的是TAnimal.Voice的代码,而后者执行的是
TDog.Voice的代码!其原因很简单,因为MyAnimal1被赋予了TAnimal类型的对象,而
MyAnimal2被赋予了TDog类型的对象。也就是说,一个TAnimal类型的变量,当它调用
Sound方法时,所执行的代码是不确定的:可能执行TAnimal.Voice的代码,也可能执行
的是TDog.Voice的代码,取决于它当时引用的是一个什么样的对象。
  再看:
  
  MyAnimal1 := TAnimal.Create;
  MyAnimal1.Sound;
  MyAnimal1.free;
  MyAnimal1 := TDog.Create;
  MyAnimal1.Sound;
  ...
  
  同一个变量MyAnimal1,在第一次调用Sound方法时,执行的是TAnimal.Voice的代
码,在第二次时执行的是TDog.Voice的代码。MyAnimal1.Sound这行代码不需要变化,
程序可以根据不同的情况赋予该变量不同的对象,从而使它执行不同的代码。这就是多
态的定义。

  这个非常重要的特点大大地增加了代码的可复用性。如前所述,只需要简单地写下
一行代码,就可以让程序执行不同的功能,因为这个虚拟方法同TAnimal的任何派生类
都是兼容的,甚至连那些还没有编写出来的类也是一样。而程序员并不需要了解这些派
生类的细节。利用多态性写出来代码,还具有简洁和维护性好的特点。
  现在我们可以回到本文的1.2节结尾处的问题了。抽象方法本身不能够做任何事
情,必须在子类中被重载并实现,才能够完成有意义的工作。但抽象方法的存在,相当
于为父类留下了一个接口,当程序将一个子类的对象赋予父类的变量时,父类的变量就
可以调用这个方法,当然此时它运行的是相应的子类中重载该方法的代码。如果没有这
个抽象方法,父类的变量就不能调用它,因为它不能调用一个只在子类中存在、而在父
类中不存在的方法!
  
  关于OOP的介绍就到此这止。在以上这些篇幅里,介绍的只是OOP最基本的一些概
念,让读者对OOP有一定的系统认识,也为下文的讨论打好基础 。更多、更深入的东
西等待着你自己去发掘。
  本文已经多次强调OOP的重要性,这里还要强调一次:对OOP的掌握程度,在某种意
义上决定着你对Delphi世界的理解能力。
  
 
给分吧.哈哈.
 
什么啊 题目都没看 我要的是实例啊 不是文章 [:(!][:(!][:(!]
 
建议你去买这本书看看,刘艺写的,附书光盘有你想要的实例
http://www.china-pub.com/computers/subject/huodong/shidu.htm
 
我这里有本书也是叫这个名字可是我看不怎么样 太难了 isbn 7-111-12772-2
不知道是不是你说的那本
我的信箱是paulcross@126.com
谁能不能把光盘例子 简单点的给我发几个阿
 
精彩.............
 
慢慢写吧!时间长了就好了!就自然而然的写成面向对象了!
 
写个简单的给你
TOrderItem = class() //订单明细
end;

TOrderItemList = class(TObjectList) //订单明细列表
public
constructor Create(SQL: string);//传一条SQL语句进去,这是一种简单的方法,也有另外一种方法是做一个持久层,,这个对象里维持一个持久层对象,委托持久层对象去从数据库中取对象,不过这种方法一般会设计到要写一个框架,比较麻烦
end;

TOrder = class //订单
private
FDateSet: TDataSet;
function GetOrderNo: string;
procedure SetOrderNo(const value: string);
public
property DataSet: TDataSet read FDataSet write FDataset;
property OrderNo: string read GetOrderNO write SetOrderNo; //订单号
function OrderItemList: TOrderItemList read GetorderItemList;//某个订单下的所有订单明细,怎么实现自己再去研究一下,不难
end;

function TOrder.GetOrderNo: string
begin
Result := FDataSet.FieldByName('OrderNO').AsString;
end;

procedure TOrder.SetOrderNo(const Value: string)
begin
FDataSet.FieldByName('OrderNo').AsString := Value;
end;
 
接受答案了.
 
后退
顶部