黑客风云——风云网络
设为首页 加入收藏 我要投稿 网站地图
您现在的位置: 黑客风云 >> 黑客文章 >> 黑客进阶 >> 黑客编程 >> 文章正文
[推荐]delphi基础学堂-详细讲解delphi
        ★★★★★
delphi基础学堂-详细讲解delphi
文章整理发布:黑客风云 文章来源:www.05112.com 更新时间:2007-1-10
 

 

第二讲 面向对象的程序设计

  构成delphi的语言基础,严格地说,不是pascal而是object pascal;后者不是对前者的简单扩充和升级,而是思想上,结构上的全面革新。贯穿object pascal始终的概念是对象。

一、面向对象的原理
  ◇面向对象是与面向过程相比较而言的。面向过程的结构化设计方法被广泛使用,但在代码量增大时(1万行以上)有许多困难难以克服。比如全局变量不容易控制;重用性不好等。
  ◇对象是对具有独立性质的事物的抽象。我们考察对象时,只关心对象的性质和行为,而不关心对象的内部实现机制。对象的最大优点是可重用性。
  ◇对象间可以通过消息的传递来相互影响,协作完成某个特定的任务。
  ◇与对象有关的三个概念:封装,继承与多态
 
  1 封装不是简单地装起来,比如记录结构,比如子目录结构。封装的真正含义是隐藏,就是隐藏代表对象状态的内部数据。
  ◇函数象一部加工机器,而对象保存自己的状态。对象不能用函数加全局变量实现。因为对象的数据是私有的,外界不能直接访问,并且多个同类型的对象可同时存在,且状态不同。
  【模型】现实世界中的收音机。设计良好的收音机,外界的输入错误并不会破坏内部的元件。
  2 封装的内容是私有的数据和操作。但如果私有的东西完全不能被访问则没有意义。不能被外界感知的东西对外界来说就是不存在的。所以要对外界提供访问接口:属性和方法。属性和方法是对被封装的内容的曲折的反映和间接的操纵。
  3 定义对象的性质的程序文档,称为类。类并不存在于内存中。通过类可以生成或说制造出多个同属于一个类型的对象。这个过程称为实例化。
  4 用于描述多种对象的文档可能有许多相似的地方。我们可以通过继承的方法减少这种重复描述的文档。继承就是指出某个文档包含其他文档中描述的特征。
  ◇继承是文档的特征,因而只是对类而言的。对象是内存中实例,对对象而言,不存在继承的概念。当观察一个对象实例的时候,我们并不知道它是否是通过继承的方式被构造的。
  ◇对从其他文档中继承的内容,我们可以通过重新描述而替代原来的内容,或者干脆通过隐藏的方法而不向外界公布某些内容。可见继承并不一定必须是上下级的关系。只要有相似的地方,就可以使用继承,继承的目的之一是为了省写代码,即代码重用。
  ◇在定义类的时候,继承提供了极大的方便。这启发我们定义一些描述多种对象性质的抽象的类。这些类不能生成实例对象,它们的唯一作用就是为其他的类描述提供继承。
  5 多态是面向对象的精髓。一般来说,多态指对同样的消息,不同的响应。静态多态通过函数重载来实现;狭义的多态指在编译时无法确定要调用的函数的地址的现象。

  【模型】从劳动者类继承的农民,工人和其他劳动者,从农民类继承的青年农民中,有些定义了劳动这个动作。见图。

劳动
劳动

劳动者
农民
工人
其他
青年农民

         ◇形成多态的前提:抽象地看待一个对象。把农民,青年农民,工人都抽象地当作劳动者来处理。这种处理方法叫做泛化。
◇在调用一个被泛化的对象的方法时,一般我们并不是希望调用抽象类的方法,而是希望执行对该对象而言,最具体的方法。因为在编译时无法确定被泛化的对象的真正类型,因而就无法确定要执行的函数的地址,从而产生多态的现象。
◇多态的实施是通过在运行时保存类型信息的办法来实现的。类型信息是关于类的全体的公共信息,不是专属于某一个特定的对象。类型信息间有继承关系,就是对类的继承关系的计算机表示。
◇多态的方法使我们站在概括的高度来处理对象,抽象思维是人类的特点和优点,多态的使用充分发挥了这种优势。我们在抽象设计的时候,可以把注意力放在对象间的本质联系上,可以不考虑任何具体特征,这种设计方法称为蓝图设计。
◇继承的目的之二就是要产生多态的能力。这是继承的重要目标。
 
6 面向过程的设计方法=数据结构+算法;面向过程的设计方法=对象+消息
【模型】奴隶社会和现代社会的对比。在生产资料少之又少的时候,集中管理更有效,在生产极大发展的时候,分布式的对象模型更有潜力和稳定性。
◇设计原则:
①对象要有独立的特点,就是不依赖或较少地依赖于外界。用术语表达,就是对象要有确定的边界。边界把对象的内部和外界环境严格地区分开。换句话说,我们必须能够明确地指出哪些属于对象,哪些不属于对象。
②对象要能完成相对独立的功能。对象的定义一般从功能的定义入手。
③对象要保护自己的数据,使外界无法干涉对象的独立性。同时,对象的方法应该操作自己的数据,而不是去他人的数据。
 
④对象对外界的影响是通过向其他对象发送消息来实现的。尽量避免直接操作外部的数据结构。
7 面向对象的设计并不完全排斥面向过程结构化方法。体现在:
①一般大型工程项目都采用了自顶向下与自底向上相结合的方法。好比盖楼房。
②在每个对象实现过程中仍然要使用结构化的设计方法。
 
8 构建类的时候,注意考虑对未来重用性能。尽量能构造成树型的类群,以便将来从合适的节点继承形成新类。
 
9 面向对象的思想的实现要兼顾到运行效率的问题。就效率而言:
c++ > delhi > java > smallTalk。就面向对象的彻底性而言,则刚好相反。
 
二、面向对象的delphi实现
  1 类的定义  type 类名=class(父类) ... end;
  ◇不写父类,表示从TObject 继承,这时,括号也不写
  ◇不支持多重继承。可以通过支持接口的方法实现多重继承的类似功能。
  2 保护方式  关键字 private protected public published
  可以把类的用户分成三组:①自己:就是类的成员函数 ②子孙:就是从本类直接或间接继承的类的成员函数 ③他人:就是除此以外的其他函数。
 
 
private
protected
public
Published
自己
允许
允许
允许
允许
子孙
禁止
允许
允许
允许
他人
禁止
禁止
允许
允许
  
  ◇published与public有相同的保护效果。但publised修饰的成员可以在设计时出现在对象浏览器中。注意,这不是delphi的内部功能,因为对象浏览器也是用delphi写的。
  ◇有一个特例:处于同一单元的定义,可以自由访问,忽略保护方式描述字
  ◇保护方式也是一个前缀,不是域的概念,后边无冒号,注意与C的区别。
  3 类操作符――is和as
    ①is的用法:表达式“对象is类”返回一个布尔类型。如,猫是猫类;猫是动物类。
  ②as的用法:表达式“对象as类”返回该对象,但类型已经强制为后边的类所指定的类型。这种表达方法叫特化。
  ③二者经常联合使用。if a is TA then (a as TA).caption := ...
    【例】多个控件连接到同一个事件,怎样在事件中区分是哪个控件发出的事件?不同类型的判断要用is,相同类型的判断要用as配合tag属性。
  4 对象存储结构
  在内存中,只存储对象的私有数据,而方法只有一个拷贝,与其他函数一起存于静态空间。对象在定义的时候,只是一个指针(4字节)并无对象的实体。必须调用类的构造方法才能生成对象。该对象存于堆空间中。对象在使用完毕后,需要调用它的析构函数来完成对内存的释放。在delphi中,一般不直接调用析构函数,而是调用free函数,这是更安全的方法。
  5 类引用
  类引用是指向类的公共信息的类型。类引用包含了类的名字,父类引用,对象占用多少内存等信息。类引用的使用是为了在运行的时候,获得关于对象的类的特征的描述。
  定义:type 标识符 = class of 类名;
  ◇在早期的面向对象语言中无类型信息的概念。这些信息只有编译器知道,而无法被运行中的代码获得。运行时类型信息,就是资料中常提到的RTTI。
  【例】取得对象的RTTI .instanceSize返回整数;.classparent返回父类型信息。
  6 构造函数
  构造函数是特殊的函数,严格地说它不是成员函数,构造函数完成内存申请和初始化成员变量的作用。构造顺序是先构造祖先,再构造自己。
  ◇定义: constructor create;或 constructor create(参数列表);
  ◇构造函数的实现中一般多要先调用祖先的构造函数。方法是:inherited create..
定义两个构造函数时,要用overload关键字表示重载,这是静态多态的例子。
7 析构函数
析构函数是成员函数。析构函数是动态多态的典型例子。当一个对象被抽象地处理时,一定要调用属于这个对象的类所定义的析构函数。而不是抽象类的析构函数。析构的顺序是,先析构自己,再析构祖先,和构造正相反。
定义:destructor destroyoverride
在实现的时候,不写override这个关键字指示着该名字的函数在运行中要做动态多态处理。
  ◇一般来说,析构函数的末尾总是调用祖先的析构函数。inherited;多态特征的函数不需要指明要调用的祖先函数名。
  ◇与C++不同,delphi的构造函数和析构函数都不会被编译器自动调用,必须显式地调用。
  8 替换与覆盖
  替换是根据局部优先的原则,用同名的成员代替祖先定义的成员。这是为了屏蔽祖先定义的某个方法或数据。覆盖则是把祖先定义的方法具体化,或说特化。这是实现多态的基础。
  实现覆盖的方法:①父类声明中加 virtual;②子类声明中加override;③子类实现中不加。
◇用dynamic virtual 具有同样的逻辑含义,只是实现的方式不同。
◇有时,覆盖的方法也称虚方法或动态方法。
9 抽象类型
只定义不实现的虚方法称抽象方法。至少含有一个抽象方法的类称为抽象类。抽象类型不能实例化,只能供给其他类做继承之用。
10 属性
属性就是对象的性质的外部表示或外部视图。属性的使用贯彻了面向对象中的封装的思想,是delphi与C++相区别的地方。理解了属性的概念,就理解了封装。
◇定义:property 变量:类型;read 读方法 write 写方法;
◇这样的定义使外界看起来,好象该对象有一个成员变量。实际上,这个变量是虚拟的,在内存中并不存在,当涉及到它的读取时,实际上调用了读方法;当涉及到它的赋值时,实际上调用了写方法。
◇属性就象对象的私有数据的一个影子。它提供了对私有数据的访问的同时提供了保护。
  【例】 type TA=class   //类的定义开始
private fx: integer;   // 私有数据
          private function getx: integer;  //读方法
                 procedure setx(n: integer);  //写方法
published property x:integer; read getx write setx;  //属性的定义
end;    //类的定义结束
var a: TA; ....
a.x := 5;等价于 a.setx(5);   c:=a.x;等价于 c:=a.getx;
    ◇属性可以直接与私有变量关联。
   published property x:integer; read fx write setx;  //属性的定义
  ◇有read 无write 时,表明该属性是只读属性。
  ◇所谓事件也是属性,只不过它的值是指针,是方法指针,指向用户定义的某个方法。
 
三、类开发方法提示
  1 分析需要的功能,从delphi类库中找到最合适的节点来继承。
  2 可以从可视化的构件继承。不一定要注册,不注册的控件可以正常在程序中使用。
  【例】自己定义一个中国Edit控件。特点是回车键当Tab键来使用。
  type TMyEdit=class(TEdit) 
private procedure WndProc(var msg: TMessaeg); override; 
end; //类的定义部分。在其中覆盖了父类的虚拟方法 WndProc
    procedure TMyEdit.WndProc(var msg: TMessage); //实现的时候,不要写 override
    begin if (msg.msg = wm_keyDown) and (msg.wParam = vk_Return) then
parent.perform(wm_nextDlgCtl,0,0)
           else inherited; 
 end; //当按了回车键的时候,给父窗口发消息,移动焦点到下一个控件
 3 类的生成还有一种方法,就是嵌入另一个控件作为自己的成员数据。用这种办法生成的类叫做包装类。使用包装类,一般是把被包装的控件当作工具来使用,而不是特化的过程。
 【练习】包装一个时钟类,显示日期和时间。从TPanel继承,把TTimer作为成员数据
 
 

上一页  [1] [2] [3] [4] [5] [6] [7] [8] 下一页  

文章录入:cainiaowang    责任编辑:cainiaowang 
【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
VIP 专 区
Copyright @2006 黑客风云 ●业务联系:QQ 联系怪人 联系奇人 Email:给怪人发邮件 给奇人发邮件
ICP备案:冀06009886