Delphi程序设计之--惯用法[2]

[入库:2005年8月18日] [更新:2007年3月25日]

本文简介:选择自 l_xiaofeng 的 blog

*//

{ no. 8 } 使用函数指针,减少单元项目包含

//我经常的认为减少单元的包含,是做公共单元的第一步,所以在如何尽量减少单元包含
//也就是如何减少程序单元的耦合性上,应多下工夫。

{ 情景描述:
tmyformmanager: 窗体管理类
tmyform:数据窗体基础类
tmyformaccess:窗体信息保存和读取类。将窗体信息保存到数据库或其他什么类型的结构中
分析:
1、窗体基础类(tmyform) 和 窗体管理类(tmyformmanager)需要在一个单元 umanagers中实现。
2、窗体具体实现类(tmyimageform)单元 fmyimange 需要包含单元umanagers,进行窗体继承,和窗体管理。
3、窗体数据读取类(tmyformaccess)单元 umyaccess 需要包含单元umanagers和单元fmyimange
问题:
 如果我希望实现窗体保存,那么应该在窗体的某个按钮事件中实现。则涉及到窗体单元需要包含窗体数据访问类单元,而如果放在窗体基础类中,则单元umanager又必须包含单元umyaccess。
 当数据访问,即数据存储格式会根据要求而改变并要求可扩充时,则单元包含必定是一个隐患。
解决办法:使用函数指针变量。
1、在单元umanagers中定义一个,保存数据信息的函数指针变量。
2、在应用程序初始化的时候给这个函数指针变量赋值。
3、在需要保存窗体信息时,判断如果指针不为空,则执行函数保存窗体信息。

{ no. 9 } 常量,认识常量,使用常量
有很多书都都介绍了常量定义的重要性,我也会经常想到,但是看看vcl源码才知道,自己忽略了,别人对常量的使用情况。

1、我们经常使用的消息的定义就是:声明一个常量,然后在适当的时候使用之。
 通常定义和使用:
 const
   wd_mymessage = wm_user + 101;
 type
 tmyform = class(tform)
 ...
   procedure wdmymessage(var message: tmessage); message wd_mymessage; {响应消息位置}
 end;
 但是,如果您将{响应消息位置}语句改写为:
   procedure wdmymessage(var message: tmessage); message wm_user + 101;
 同样,编译可以成功,使用也正常。所以,常量定义在window系统处理和接口中应用非常普遍。

2、在delphi中,我们定义了颜色变量,clred, clgreen等,也都是定义的常量,便于以后的使用。通过这个观察我发现,常量的定义应该是在项目中,可部分复用的,所以,可以定义一个标准常量单元,以便在个项目中,复用定义的常量。

 

 { no. 10 }一个delphi中,常用到的数组

对tidentmapentryd类型的数组定义和使用,delphi中,有比较完善的实现。
 tidentmapentry = record
   value: integer;
   name: string;
 end;

1、数组定义:array[0..arrmax] of tidentmapentry
 可参考:controls单元中:
 cursors: array[0..21] of tidentmapentry = (
 ...
 );
2、两个互相求值得函数: inttoident(由value求name)和 identtoint(由name求value);
 具体应用可以参考:identtocursor 和 cursortoident。

3、应用:a、直接应用此树组定义方式和数组操纵函数;b、学习函数中,对数组访问和操纵的方式。c、学习标准的信息访问函数定义: function inttoident(int: longint; var ident: string; const map: array of tidentmapentry): boolean; 具体返回的信息由参数方式返回回来,至于访问是否有效,则通过函数的布尔返回值加以判断。

 

{ no. 11 } 由特例到普通的发现
我通过对 cursors 的定义和操作函数的跟踪发现:
1、如 { no. 10 }中介绍的,将cursors的定义和一般操作通用化。
2、提供 int 和 ident互转化的函数。
3、提供数组列表信息循读取的函数: getcursorvalues;其中,使用了 { no. 3 } 中介绍的“事件指针 做参数”读取列表信息的方法。

{ no. 6 } 的补充:
例子:
procedure registercomponents(const page: string;
 componentclasses: array of tcomponentclass);
begin
 if assigned(registercomponentsproc) then
   registercomponentsproc(page, componentclasses)
 else
   raise ecomponenterror.createres(@sregistererror);
end;

解读:
1、使用注册的方式,记录可使用的控件的类型等。
3、对于 registercomponentsproc 使用了{ no. 8 } 中“使用函数指针,减少单元项目包含”的方法,便于将来程序的扩充,版本的升级等。

 

 { no. 11 }只定义一个公共函数
//项目描述:现在要实现一个cad画图或visio系统,要求有好的扩展性和易维护性;
//并且要求耦合性低,便于,将来系统的部分或扩展后的系统封装后,直接在今后的项目中使用

设计:
1、设计一个图形对象抽象类,在此类中,定义一个抽象函数 cadperform,函数的参数参照function tcontrol.perform(msg: cardinal; wparam, lparam: longint): longint;
2、在图形管理类中,实现一个图形对象列表的管理,列表中保存的是抽象对象的指针。
3、对于要对具体类对象进行操纵控制时,只需通过条用canperform函数,然后根据当前操作的类别传入 msg, 并传入相应的参数信息。

实现: tcad 为由抽象类继承下来的第一层控件类
function tcad.cadperform(msg: cardinal; wparam, lparam: longint): longint;
begin
 case msg of
 my_message1: result := mymessage1(wparam, lparam);
 my_message2: result := mymessage2(wparam, lparam);
 end;
end;
对于,tpoint继承自 tcad, cadperform函数实现如下。
function tpoint.cadperform(msg: cardinal; wparam, lparam: longint): longint;
begin
 case msg of
 my_message1: result := mymessage1(wparam, lparam); //屏蔽了tcad中此操作类型的处理
 my_message3: result := mymessage3(wparam, lparam);
 else result := inherited cadperform(msg, wparam, lparam);
 end;
end;

*说明:
因为,我们对图形对象的操作会非常频繁,所以我们通过定义一个公共开放的接口函数来实现,类的高封装性和程序的易维护性、好扩展等性能。
*//

 

{ no. 12 }

以下是我编程时的要求:(部分信息没有语言限制)
//以下的解决方案,几乎都可以在上面的方法中,找到
1、减少程序的复杂度。a、减少函数个数,使用case、tag方式,学习实现perform定义方式;b、减少单元嵌套关系,使用消息传递方式,减少窗体单元的互相包含。
2、减少

 

{ no. 13 }使用广播,实现管理类对管理列表对象的通知

//对于{ no. 12 } 项目描述中,当画图的窗体控件属性或状态改变时,经常会需要通知所有的图形对象,进行相应的改变。
//则如果只定义一个广播函数,就可以实现父子通知的话,也会提高程序的可重用性、扩展性、易维护性等,使类结构清晰。

本文关键:经验,技巧
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top