发信人: CF (往事随风飘), 信区: BorlandDev
标  题: Delphi面向对象编程的20条规则
发信站: 哈工大紫丁香 (2003年05月15日10:27:13 星期四), 站内信件

 转自fixdown

::本类阅读TOP10::


        Delphi面向对象编程的20条规则


作者:不详  来源:不详  加入时间:2003-5-14  阅读次数:

阅读选项:  自动滚屏[左键停止] 


规则一:为每一个类创建一个单元(One Class,One Unit)
请始终牢记这一点:类的私有(private)和保护(protected)的部分只对于其他单元
中的类和过程(procedure)才是隐藏的.因此,如果你想得到有效的封装性,你应
该为每一个类使用一个不同的单元。对于一些简单的类,比如那些继承其他类的类
,你可以使用一个共享的单元。不过共享同一个单元的类的数目是受到限制的:不
要在一个简单的单元里放置超过20个复杂的类,虽然Borland公司的VCL代码曾经这
样做过。
如果你使用窗体的时候,Delphi会默认的遵循“一个类使用一个单元”的规则,这
对于程序员来说也是十分方便的。当你向你的项目中添加一个没有窗体的类时,
Delphi也会创建一个新的独立的单元。
规则二:为组件命名(Name Components)
为每一个窗体和单元给出一个有意义的名字是十分重要的。窗体和单元的名字必须
是不同的,不过我趋向于为他们两者使用相似的名字,如对于关于窗体和单元可以
为他们使用AboutForm 和About.pas.
为组件使用带有描述性的名字同样十分重要。最常见的命名方式是使用类的小写字
母开头,再加上组件的功能,如BtnAdd 或者editName。采用这样的命名方式为组
件命名可能会有很多相似的名字,而且也没有一个最好的名字,到底应该选择那一
个应该依据你的个人爱好而定。
规则三:为事件命名(Name Events)
对于事件处理方法给出合适的名字更加重要。如果你对于组件给出了一个合适的名
字,那么系统默认的名字ButtonClick将变成BtnAddClick。虽然从这个名字中我们
可以猜到这个事件处理程序的功能,但是我认为使用一个能够描述该方法的作用的
名字,而不是采用Delphi附加的名字是一种更好的方式。例如,BtnAdd按钮的
OnClick事件可以命名成AddToList。这会使得你的程序可读性更强,特别是当你在
这个类的其他方法中调用这个事件处理程序时,而且这会帮助程序员为类似的事件
或是不同的组件选用相同的方法。不过我必须声明,使用动作(Actions)是目前
开发重要的程序时我最喜欢的方法。
规则四:使用窗体方法(Use Form Methods)
窗体都是一些类,因此窗体的代码是以方法组织的。你可以向窗体中添加事件处理
程序,这些处理程序完成一些特别的功能,而且他们能被其他方法调用。除了事件
处理方法外,你还可以向窗体添加完成动作的特别定义的方法以及访问窗体状态的
方法。在窗体中添加一些公共的(Public)方法供其他窗体调用要比其他窗体直接
操作他的组件要好。
规则5:添加窗体构造器(Add Form Constructors)
在运行时创建的第二个窗体除了一个默认的构造器(从Tcomponent 类继承而来)
外还会提供其他特殊的构造器。如果你不需要考虑和Delphi4以前的版本的兼容性
问题,我建议你重载(Overload)Create方法,添加必要的初始化参数。具体代码
可参见下面的代码:
Public
Constructor Create(Text:string): reintroduce ; overload;
Constructor TformDialog.Create(Text:string);
Begin
Inherited Create(Application);
Edit1.Text:=Text;
End; 
规则6:避免全局变量(Avoid Global Variables)
应该避免使用全局变量(就是那些在单元的interface 部分定义的变量)。下面将
会有一些建议帮助你如何去做。
如果你需要为窗体存储额外的数据,你可以向窗体类中添加一些私有数据。这种情
况下,每一个窗体实例都会有自己的数据副本。你可以使用单元变量(在单元的
implementation部分定义的变量)声明那些供窗体类的多个实例共享的数据。
如果你需要在不同类型的窗体之间共享数据,你可以把他们定义在主窗体里来实现
共享,或者使用一个全局变量,使用方法或者是属性来获得数据。

规则7:永远不要在Tform1类中使用Form1(Never Use Form1 in Tform1)
你应该避免在类的方法中使用一个特定的对象名称,换句话说,你不应该在
TForm1类的方法中直接使用Form1.如果你确实需要使用当前的对象,你可以使用
Self关键字。请牢记:大多数时候你都没有必要直接使用当前对象的方法和数据。

如果你不遵循这条规则,当你为一个窗体类创建多个实例的时候,你会陷入麻烦当
中。

规则8:尽量避免在其他的窗体中使用Form1(Seldom Use Form1 In Other Forms 
)
即使在其他窗体的代码中,你也应该尽量避免直接使用全局变量,如Form1.定义一
些局部变量或者私有域供其他窗体使用会比直接调用全局变量要好。
例如,程序的主窗体能够为对话框定义一个私有域。很显然,如果你计划为一个派
生窗体创建多个实例,这条规则将是十分有用。你可以在主窗体的代码范围内保持
一份清单,也可以更简单地使用全局Sreen对象的窗体数组。
规则9:移除Form1(Remove Form1)
事实上,我的建议是在你的程序中移除Delphi自动创建的全局窗体对象。即使你禁
止了窗体的自动添加功能,这也有可能是必要的,因为在Delphi随后仍然可能添加
这样的窗体。我给你的建议是应该尽量避免使用全局窗体对象。
我认为对于Delphi新手而言,移除全局窗体对象是十分有用的,这样他们不至于对
类和全局对象两者的关系感到疑惑。事实上,在全局窗体对象被移除后,所有与它
有关的代码都会产生错误。

规则10:添加窗体属性(Add Form Properties)
正如我已经提到过的,当你需要为你的窗体添加数据时,请添加一个私有域。如果
你需要访问其他类的数据,可以为你的窗体添加属性。使用这种方法你就能够改变
当前窗体的代码和数据(包含在它的用户界面中)而不必改变其他窗体或类的代码。

你还应该使用属性或是方法来初始化派生窗体或是对话框,或是访问他们的最终状
态。正如我前文所说的,你应该使用构造器来完成初始化工作
规则11:显示组件属性(Expose Components Properties)
当你需要访问其他窗体的状态时,你不应该直接访问它的组件。因为这样会将其他
窗体或其它类的代码和用户界面结合在一起,而用户界面往往是一个应用程序中最
容易发生改变的部分。最好的方法是,为你需要访问的组件属性定义一个窗体属性
。要实现这一点,可以通过读取组件状态的Get方法和设置组件状态的Set方法实现

假如你现在需要改变用户界面,用另外一个组件替换现有的组件,那么你只需做的
是修改与这个组件属性相关的Get方法和Set方法,而不必查找,修改所有引用这个
组件的窗体和类的源码。详细实现方法请参见下面的代码:
private
function GetText:String;
procedure SetText(const Value:String);
public
property Text:String;
read GetText write SetText;
function TformDialog.GetText:String;
begin
Result:=Edit1.Text;
end;
procedure TformDialog.SetText(const Value:String);
begin
Edit1.Text;=Value;
end;
规则12:属性数组(Array Properties) 
如果你需要处理窗体中的一系列变量,你可以定义一个属性数组。如果这些变量是
一些对于窗体很重要的信息,你还可以把他们定义成窗体默认的属性数组,这样你
就可以直接使用SpecialForm[3]来访问他们的值了。
下面的代码显示了如何将一个listbox组件的项目定义成窗体默认的属性数组。
type
TformDialog =class(TForm)
private
listItems:TlistBox;
function GetItems(Index:Integer):String;
procedure SetItems(Index:Integer:const Value:String);
public
property Items[Index:Integer]:string;
end;
function TFormDialog.GetItems(Index:Integer):string;
begin 
if Index >=ListItems.Items.Count then 
raise Exception.Create(‘TformDialog:Out of Range’);
Result:=ListItems.Items[Index];
end;
procedure TformDialog.SetItems(Index:Integer;const alue:string);
begin 
if Index >=ListItems.Items.Count then
raise Exception.Create(‘TformDialog:Out of Range’);
ListItems.Items[Index]:=Value;
end;
规则13:使用属性的附加作用(Use Side-Effects In Properties)
请记住:使用属性而不是访问全局变量(参见规则10、11、12)的好处之一就是当
你设置或者读取属性的值时,你还可能有意想不到的收获。
例如,你可以直接在窗体界面上拖拉组件,设置多个属性的值,调用特殊方法,立
即改变多个组件的状态,或者撤销一个事件(如果需要的话)等等。

规则14:隐藏组件(Hide Components)
我经常听见那些面向对象编程的狂热追求者抱怨Delphi窗体中包含一些在
published部分声明的组件,这是和面向对象思想的封装性原理不相符合的。他们
确实提出了一个重要的议题,但是他们中的大多数人都没有意识到解决方法其实就
在他们手边,完全不用重写Delphi代码,也不用转向其他语言。
Delphi向窗体中添加的组件参照可以被移到private部分,使得其他窗体不能访问
他们。如果你这样做,你就有必要设置一些指向组件的窗体属性(请参见规则11)
,并且使用它们来访问组件的状态。
Delphi将所有的这些组件都放在published部分,这是因为使用这种方式能够保证
这些域一定是在.DFM文件中创建的组件。当你改变一个组件的名称时,VCL能够自
动地将这个组件对象与它在窗体中的参照关联起来。因为delphi使用RTTI和
Tobject方法来实现这种功能,所以如果想要使用这种自动实现功能,就必须把参
照放置在published部分(这也正是为什么delphi将所有的组件都放在published部
分的缘故)。
如果你想知道的更详细一点,可以参看下面的代码:
procedure Tcomponent.SetReference(Enable:Boolean);
var
Field:^Tcomponent;
begin
If Fowner<> nil then begin
Field:=Fowner.FieldAddress(Fname);
If Field<>nil then
Field^:=Self
else
Field^:=nil;
end;
end;
上面的代码是Tcomponent类的SetReference方法,这个方法可以被
InserComponent,RemoveComponent和SetName等方法调用。
当你理解了这一点后,你应该不难想到如果你将组件参照从published部分移到了
private段,你将失去VCL的自动关联功能。为了解决这个问题,你可以通过在窗体
的OnCreate事件中添加如下代码解决:
Edit1:=FindComponent(‘Edit1’) as Tedit;
你接下来应该做的就是在系统中注册这些组件类,当你为他们注册过后就能使
RTTI包含在编译程序中并且能够被系统所使用。当你将这些类型的组件参照移到
private部分时,对于每一个组件类,你只需为他们注册一次。即使为他们注册不
是一定必要的时候,你也可以这样做,因为对于RegisterClasses的额外调用有益
无害。通常你应该在单元中负责生成窗体的初始化部分添加以下的代码:
RegisterClass([TEdit]);

规则15:面向对象编程的窗体向导(The OOP Form Wizard)
为每一个窗体的每一个组件重复上述两个操作不仅十分的烦人,而且相当的浪费时
间。为了避免额外的负担,我已经为此写了一个简单的向导程序。这个程序将会生
成一些可以完成以上两步工作的代码,你需要做的仅仅是做几次复制和粘贴就行了

遗憾的是这个向导程序不能自动将代码放置到单元中合适的地方,我目前正在修改
这个向导程序,希望能实现这个功能。你可以到我的网站(www.marcocantu.com)
查找更加完善的程序。
规则16:可视化窗体继承(Visual Form Inheritance)
如果应用得当,这将是一个强大的工具。根据我的经验,你所开发的项目越大,越
能体现它的价值。在一个复杂的程序中,你可以使用窗体的不同等级关系来处理一
组相关窗体的多态性(polymorphism)。
可视化窗体继承允许你共享多个窗体的一些公共的动作:你可以使用共享的方法,
公用的属性,甚至是事件处理程序,组件,组件属性,组件事件处理方法等等。

规则17:限制保护域数据的使用(Limit Protected Data)
当创建一些具有不同分级体系的类时,一些程序员趋向于主要使用保护域,因为私
有数据不能被子类访问。我不能说这没有其合理性,但是这肯定是和封装性不相容
和的。保护数据的实现能够被所有继承的窗体所共享,而且一旦这些数据的原始定
义发生改变,你必须更改所有的相关部分。
请注意,如果你遵循隐藏组件这样一条规则(Rule 14),继承窗体就不可能访问
基类的私有组件。在一个继承窗体中,类似Edit1.Text:=’’的代码就不会被编
译。虽然这是相当的不方便,但是至少在理论上这是值得肯定的事情,而不是否定
的。如果你感觉到实现封装性是最主要,最需要的,就请将这些组件参照放在基类
的私有段。
规则18:保护域中的访问方法(Protected Access Methods)
在基类中将组件参照放置在私有域中,而为这些组件添加一些访问函数来得到他们
的属性,这将是一种更好的方法。如果这些访问函数仅仅在这些类内部使用而且不
是类接口的一部分,你应该在保护域声明他们。例如Rule 11中描述过的GetText和
SetText方法就可以声明成protected,并且我们可以通过调用SetText(’’)来
编辑文本。
事实上,当一个方法被镜像到一个属性时,我们可以简单地采用如下代码就可以达
到编辑文本地目的:Text:=’’;
规则19:保护域中的虚拟方法(Protected Virtual Methods)
实现一个灵活的分级制度的另一个关键点是定义一些你可以从外部类调用的虚拟方
法来得到多态性。如果这个方法使用得当,将会很少出现其他公共的方法调用保护
域中的虚拟方法的情况。这是一个重要的技巧,因为你可以定制派生类的虚拟方法
,来修改对象的动作。
规则20:用于属性的虚拟方法(Virtual Methods For Properties)
即使是访问属性的方法也能定义成virtual,这样派生类就能改变属性的动作而不
必重定义他们。虽然这种方法在VCL当中很少使用,但是它确实十分灵活、强大。
为了实现这一点,仅仅需要将Rule 11当中的Get 和Set 方法定义成Virtual。基类
的代码如下所示:
type
TformDialog = class ( TForm)
Procedure FormCreate(Sender:Tobject);
Private
Edit1:Tedit;
Protected 
function GetText:String;virtual;
procedure SetText(const Value:String);virtual;
public
constructor Create(Text :String):reintroduce;overload;
property Text:String read GetText write SetText;
end;
在继承窗体中,你可以添加一些额外的动作来重载虚拟方法SetText:
procedure TformInherit.SetText(const Value:String);
begin
inherited SetText(Value);
if Value=’’ then
Button1.Enabled:=False;
end;

小结
要做到一个好的Delphi面向对象编程程序员远非我在上面提到的这些规则这么简单
。上面的这20条规则中有一些可能需要足够的耐性和时间来实现,因此,我也不能
强求你能遵循所有的这些规则。但是这些规则应该被合适的运用到你的程序中,而
且当你开发的应用程序越大,参与的程序员越多,这些规则越重要。不过,即使是
一些小程序,始终记住这些规则并在合适的地方使用他们也会对你有所帮助。
当然,还有很多其他的经验规则我没有涉及到,特别是存储器处理和RTTI问题,因
为他们十分的复杂,需要专门的说明。
我的结论是要遵循我上面列出的规则会付出一定的代价,特别是额外的代码,但是
这些代价会让你得到一个更加灵活强壮的程序。希望Delphi的后续版本能够帮组我
们减少这些代价。    
 【用户评论】                       【告诉好友】                        
 【打印本页】

    

    T O P 
 




--

再不学习,你就要......

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.231.2]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:2.487毫秒