选择显示字体大小

三谈多态—善用virtual

版权所有:nicrosoft
文章来源:东日制作室

  多态性,是一种能给程序带来灵活性的东西。看过《设计模式》的程序员应该都知道,相当多的模式(几乎所有)都是依靠多态来实现的,以此给程序提供可扩展、可重用性。在《再谈多态——向上映射及vmt/dmt》一文中,提到了多态性是依赖于虚函数/虚方法(即动态绑定)来实现的,也介绍了虚函数/虚方法(virtual)的实现方法。那么本文就来谈一下,如何使用virtual、善用virtual来获取多态性给我们带来的灵活性。
  
  实例是最好的教材,因此本文还是假设一个需求,写一个实例来讲解。不过,我想没有必要给出所有源码,因此在本文中有些实现的代码会粗略带过。另外,本文所有代码均为object pascal语言编写,实现环境为delphi
  
  另外,由于“方法(method)”一词已经成为object pascal的术语,因此,以下称成员函数都为“方法”。也许c++程序员会不太适应这样的称呼(呵呵,我自己也不太适应),见谅吧。
  
  假设我们要编写一个纯文本内的编辑器,也就是记事本(呵呵,别嫌例子老套,记事本程序在相当多方面都是很好的教材),编辑控件我们一般会用tmemo或trichedit,但是它们的功能都不甚强大,也许我们目前没有,但日后会找到一个更好的第三方文本编辑控件(比如,支持语法着色的)。因此,我们必须为未来的改进留下方便之门,否则到时候再重写全部程序真是太傻了。
  
  界面层(菜单响应、状态显示等)对文本编辑器控件的控制的代码对于所有编辑器来说都是类似的,应该可以被重用。那么就必须将界面层的代码与编辑器控件的控制代码隔离开来。
  
  如何隔离?我们可以为所有的编辑器控件指定一个公共的接口(抽象类),界面层只看得到这个接口,只使用接口提供的功能,那么,我们更换任何编辑器控件时,都不必更改界面层的代码了。
  
  首先,抽象出编辑器的基本操作,如:load(打开文本)、save(保存到文件)、copy(复制到剪贴板)等等,将这些操作作为public方法。其次,考虑这些操作中有哪些会涉及到具体相关控件的,对于这些操作,你有两种选择:1、如果完全依赖控件本身的,可以选择将其定义为虚方法或抽象虚方法(即c++中的纯虚函数);2、如果只是有部分代码依赖控件本身的,将这部分操作抽象到一个protected的抽象虚方法中,而将相同的部分代码写在基类中,并由基类的方法中调用protected的抽象虚方法。
  
  如果还没有完全明白,请看代码:

teditor = class // 抽象基类
  private
   m_filename : string;
  protected
   function doload(filename : string) : boolean; virtual; abstract;
public
function load(filename : string) : boolean;
function save() : boolean;
function saveas(filename : string) : boolean; virtual; abstract;
   // ... 其他需要的操作,由需求而定
end;

  
  好,我们来详细说明一下teditor为什么是这个样子的。其有一个私有成员,保存编辑器所对应的文件名。然后看public部分,它至少提供了三个操作:load——从某文件中读取文本到编辑器;save——将文本保存到文件,文件名为m_filename所保存的;saveas——以指定的一个文件名保存文本。

  三个操作中,saveas为抽象虚方法,因为它的实际动作与每个编辑控件相关,而基类本身并不知道该如何保存文件。

  save和load都是非虚方法,其中save的任务很简单,就是将文本以m_filename所保存的文件名保存,其实现可以是调用抽象的saveas,只需要将文件名传给saveas即可。基类完全可以确定如何完成save(因为它可以保证派生类实现了saveas),因此其为非虚方法。

  而load呢?load操作的步骤是:1、将文本从文件中读取到编辑器控件中;2、将m_filename的值设置为所读取的文件名。其中第一个步骤与具体编辑器控件相关,应该是由派生类去实现它,第二个步骤派生类无法实现,因为派生类看不到私有的m_filename成员。但即使把m_filename移到protected节中,以使派生类可以访问它,也并非好办法,因为这样的话,在实现每个派生类时,都要记住设置m_filename的值,这不仅造成代码重复(每个派生类都要这样做),而且这不应该是派生类的义务。因为m_filename应该是基类的内部实现,对外不可见。因此,第二个步骤应该由基类来完成。如何达成这个目的呢?


 


关键字 本文所属关键字

相关 与本文相关文章

分类 所有文章关键字导航

源码编程相关

Java   Asp   PHP   .Net   XML   C/C++   CGI   VB   Jsp   J2ee   J2se   J2me   EJB   Servlet   Tomcat   Resin   Struts   Weblogic   Eclipse   ANT   GUI   JMS   Web servise   IDEA   Webphere   Hibernate   Spring   Jboss   Applet   Swing   Socket   Javamail   Perl   Ajax   P2P   安全   模式   框架   测试   开源   游戏

SQL数据库相关

My-SQL   Ms-SQL   Access   DB2   Oracle   Sybase   SQLserver   索引   存储过程   加密   数据库   分页   视图  

手机无线相关

3G   Wap   CDMA   GRPS   GSM   IVR   彩信   短信   无线   增值业务

网页设计制作相关

HTML   CSS   网页配色   网页特效   Javascript   VBscript   Dreamweaver   Frontpage   JS   Web   网站设计

网站建设推广相关

建站经验   网站优化   网站排名   推广   Alexa

操作系统/服务器相关

Windows XP   Windows 2000   Windows 2003   Windows Me   Windows 9.x   Linux   UNIX   注册表   操作系统   服务器   应用服务器

图形图像多媒体相关

Photoshop   Fireworks   Flash   Coreldraw   Illustrator   Freehand   Photoimpact   多媒体   图形图像

标准 网站致力的规范

Valid CSS!

无不良内容,无不良广告,无恶意代码

Valid XHTML 1.0 Transitional

creativecommons