前言
大多数Delphi程序员都像使用Visual Basic 那样使用他们手头上开发工具,而丝毫没有意识到Delphi的强大功能,更谈不上使用这些功能了。(写到这里,编辑惶恐的举起了手,怎么可能呢?)Delphi和Visual Basic不同,Delphi完全建立在面向对象结构上,这不仅影响到VCL的结构,而且影响到使用Delphi开发的每一个程序。
在本文中,我不想涉及到面向对象编程(OOP)的所有理论,只是提出一些简单的经验规则。希望这些规则能够帮助改善你的程序结构。无论你开发的是何种类型的程序,这些经验规则都是适用的。你应当把他们当作一些建议,记住他们并把他们应用到你开发的程序中去。
关于面向对象编程,我想强调的一个关键原理是封装。我们都希望创建一些灵活而且强健的类,因为这样的类允许我们以后修改他们的实现方法而不影响到程序中的其他部分,这正是封装给我们带来的好处。虽然封装不是创建一个好的面向对象程序的唯一标准,但是它构成了面向对象编程的基础,所以在本文中我也许会过多的强调封装性,请不要感到奇怪,我有足够充分的理由这么做。
最后,我想说明这样一个事实:本文将主要集中说明窗体(Forms)的开发(虽然其中的一些规则对于组件的开发同样适用),因此这些规则对于所有的Delphi程序员都是适用的。那些编写组件的程序员必须把面向对象编程和类(Class)作为核心的元素,但是对于那些使用组件编程的程序员,他们时常会忘记面向对象。对于他们,本文可以当作一个提示,提醒他们始终记住面向对象编程。
第一部分:窗体是类(A Form is A Class)(rule 1-rule 15)
程序员常常将窗体看作是对象,而事实上窗体是类。两者的差别在于你创建基于相同的窗体类的多个窗体对象。令人感到疑惑的是Delphi为你定义的每一个窗体类创建了一个默认的全局对象。这对于新手来说是相当方便的,但是这同样会使他们形成坏习惯。
第二部分:继承(Inheritance)(rule 15-rule 20)
在讲述了一系列关于类特别是关于窗体类的规则后,第二部分将是一些关于类的继承性以及可视化窗体继承的建议和技巧。
关于代码
如果你想使用这些代码,请注意构造器必要的初始化设置以及私有组件参照,同时有必要设置好窗体的OldCreateOrder属性。否则,带有组件的窗体构造器的初始化代码将在窗体的OnCreate事件之前得到执行。
规则一:为每一个类创建一个单元(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)
正如我已经提到过的,当你需要为你的窗体添加数据时,请添加一个私有域。如果你需要访问其他类的数据,可以为你的窗体添加属性。使用这种方法你就能够改变当前窗体的代码和数据(包含在它的用户界面中)而不必改变其他窗体或类的代码。
你还应该使用属性或是方法来初始化派生窗体或是对话框,或是访问他们的最终状态。正如我前文所说的,你应该使用构造器来完成初始化工作。