Delphi编程OOP思想【转】

时间:2022-03-05 01:08:20

刚刚接触的Delphi的朋友,可能最感兴趣的就是它丰富、强大的VCL(可视化构件库)。仅仅向窗体上扔几个构件,甚至不用动手写代码,就能很容易地做出一个有实用价值的程序,真是令人激动。但是,VCL只是Delphi的一小部分,Delphi的优秀远远不只是表现在VCL上。如果你仅仅停留在使用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.SetValue(1,1,2000)这一句分别为三个数据成员赋了值,而ADay.LeapYear调用则返回当前日期所在年是否为闰年。至此,整段代码的意义也就清楚了。
  然而,类不仅仅这么简单。上面这个例子是一个非常简单的类,可以直接访问它的任何成员(数据和方法)。但某些类的成员是不能被随便访问的。Delphi中用三个关键字区分这些成员的访问权限:
  
  表1