You are on page 1of 4

20 Rules For OOP In Delphi

by Marco Cantù

M ost Delphi programmers use


their development environ-
ment as they would use Visual
creates a default global object for
every form class you define. This is
certainly handy for newcomers,
way better to use a name describ-
ing the effect of the method, not
the attached component. For
Basic [Editor throws his hands up in but can turn into a bad habit. example, the OnClick event of the
horror at the mere thought!], with- btnAdd button can be named
out realising and taking advantage Rule 1: One Class, One Unit AddToList. This makes the code
of the power they have at their Always remember that the private more readable, particularly when
hands. Delphi is based on an object and protected portions of a class you call the event handler from
oriented architecture, which does are hidden only to classes and pro- another method of the class, and
not only impact the VCL structure cedures in other units. Therefore, helps developers attach the same
but also each and every Delphi if you want to have an effective method to multiple events or to
application. encapsulation you should use a different components, although I
In this article I don’t want to different unit for every class. For have to say that using Actions is
cover the theory of OOP, but just simple classes, inheriting one from currently my preferred choice for
suggest some simple rules which the other, you can actually use a non-trivial programs.
might help you improve the struc- shared unit, but only if the number
ture of your programs. These rules of classes is limited: Don’t place a Rule 4: Use Form Methods
of thumb should be considered as 20-classes complex hierarchy in a If forms are classes their code is
suggestions, to be applied or not single unit, even if Borland does it collected in methods. Besides the
depending on the actual type of in the VCL source code... event handlers, which play a spe-
application you are building. My If you think about forms, Delphi cial role but can still be called as
suggestion is simply to keep them follows the ‘one class, one unit’ other methods, it is often useful to
in mind. principle by default, which is add custom methods to form
The key principle I want to certainly handy. When adding classes. You can add methods per-
underline is encapsulation. We non-form classes to a project, forming actions and accessing to
want to create flexible and robust create new separate units. the status of the form. It is much
classes, which will allow us to better to add a public method to a
change the implementation later Rule 2: Name Components form than to let other forms
on without affecting the rest of the It is very important to give a mean- operate on its components
program. This is not the only ingful name to each form and each directly.
criterion for good OOP, but it unit. Unluckily the two names must
represents the foundation, so if I be different, although I tend to use Rule 5:
actually over-stress it in this article similar names for the two, such as Add Form Constructors
I have some good reasons to do so. AboutForm and About.pas. A secondary form created at
Finally, to underline the fact that It’s important to use descriptive runtime can provide other specific
these principles should be used in names for components too. The constructors beside the default
our daily work by all of us Delphi most common notation is to use a one (inherited form the TComponent
programmers, I’m going to focus few lower case initial letters for the class). If you don’t need compati-
mainly on the development of class type, followed by the role of bility with versions of Delphi prior
forms, even if some of the rules the component, as in btnAdd or to 4, my suggestion is to overload
equally apply to the development editName. There are actually many the Create method, adding the
of components. Those who write similar notations following this required initialisation parameters.
components must consider OOP style and there is really no reason Listing 1 gives an example.
and classes as a central element. to say any one of them is best, it’s
Those who use components at up to your personal taste. Rule 6:
times forget about OOP: this article Avoid Global Variables
can be considered as a reminder. Rule 3: Name Events Global variables (that is, variables
It is even more important to give declared in the interface portion
Part 1: A Form Is A Class proper names to event handling of a unit) should be avoided. Here
Programmers usually treat forms methods. If you name the compo- are a few suggestions to help you
as objects, while in fact they are nents properly, the default name do this.
classes. The difference is that you of Button1Click, for example, If you need extra data storage for
can have multiple form objects becomes btnAddClick. Although we a form, add some private fields to
based on the same form class. The can guess what the method does it. In this case each form instance
confusing thing is that Delphi from the button name, I think it is will have its own copy of the data.

8 The Delphi Magazine Issue 47


You might use unit variables public
constructor Create (Text: string); reintroduce; overload;
(declared in the implementation constructor TFormDialog.Create(Text: string);
portion of the unit) for data shared begin
inherited Create (Application);
among multiple instances of the Edit1.Text := Text;
end;
form class.
If you need data shared among
➤ Listing 1
forms of different types, you can
share them by placing the data in
the main form, or in a global object, private
and use methods or properties to function GetText: String;
procedure SetText(const Value: String);
access the data. public
property Text: String
read GetText write SetText;
Rule 7: function TFormDialog.GetText: String;
begin
Never Use Form1 In TForm1 Result := Edit1.Text;
end;
You should never refer to a specific procedure TFormDialog.SetText(const Value: String);
object in a method of the class of begin
Edit1.Text := Value;
that object. In other words, never end;
refer to Form1 in a method of the
TForm1 class. If you need to refer to
➤ Listing 2: You can add a property to a form to expose a property of
the current object, use the self
a component.
keyword. Keep in mind that most of
the time this is not needed, as you
can refer directly to methods and and the global object anymore. In Suppose you now change the
data of the current object. fact, after the global object has user interface, replacing the com-
If you don’t follow this rule, been removed, any reference to it ponent with another one. All you
you’ll get into trouble when you will result in an error. have to do is fix the Get and Set
create multiple instances of the methods related with the prop-
form. Rule 10: Add Form Properties erty, you won’t have to check and
As I’ve already mentioned, when modify the source code of all the
Rule 8: Seldom Use you need data for a form, add a pri- forms and classes which might
Form1 In Other Forms vate field. If you need to access this refer to that component. You can
Even in the code of other forms, try data from other classes, then add see an example in Listing 2.
to avoid direct references to global properties to the form. With this
objects, such as Form1. It is much approach you will be able to Rule 12: Array Properties
better to declare local variables or change the code of the form and its If you need to handle a series of
private fields to refer to other data (including its user interface) values in a form, you can declare
forms. without having to change the code an array property. In case this is an
For example, the main form of a of other forms or classes. important information for the form
program can have a private field You should also use properties you can make it also the default
referring to a dialog box. Obviously or methods to initialise a second- array property of the form, so that
this rule becomes essential if you ary form or dialog box, and to read you can directly access its value by
plan creating multiple instances of its final state. The initialisation writing SpecialForm[3].
the secondary form. You can keep can also be performed using a Listing 3 shows how you can
a list in a field of the main form, or constructor, as I have already expose the items of a listbox as the
simply use the Forms array of the described. default array property of the form
global Screen object. hosting it.
Rule 11: Expose
Rule 9: Remove Form1 Components Properties Rule 13:
Actually, my suggestion is to When you need to access the Use Side-Effects In Properties
remove the global form object status of another form, you should Remember that one of the advan-
which is automatically added by not refer directly to its compo- tages of using properties instead of
Delphi to the program. This is pos- nents. This would bind the code of accessing global data is that you
sible only if you disable the auto- other forms or classes to the user can cause side-effects when writ-
matic creation of that form (again interface, which is one of the por- ing (or reading) the value of a
added by Delphi), something tions of an application subject to property.
which I suggest you should get rid most changes. Rather, declare a For example, you can draw
of anyway. form property mapped to the com- directly on the form surface, set
I think that removing the global ponent property: this is accom- the values of multiple properties,
form object is very useful for plished with a Get method that call special methods, change the
Delphi newcomers, who then won’t reads the component status and a status of multiple components at
get confused between the class Set method that writes it. once, or fire an event, if available.

10 The Delphi Magazine Issue 47


type
this call even if it is not required, as
TFormDialog = class(TForm) an extra call to the RegisterClasses
private
ListItems: TListBox; method is harmless. The Register-
function GetItems(Index: Integer): string;
procedure SetItems(Index: Integer; const Value: string); Classes method is usually added to
public the initialization section of the
property Items[Index: Integer]: string read GetItems write SetItems; default;
end; unit hosting the form:
function TFormDialog.GetItems(Index: Integer): string;
begin
if Index >= ListItems.Items.Count then RegisterClasses([TEdit]);
raise Exception.Create('TFormDialog: Out of Range');
Result := ListItems.Items [Index];
end;
Rule 15:
procedure TFormDialog.SetItems(Index: Integer; const Value: string);
begin The OOP Form Wizard
if Index >= ListItems.Items.Count then
raise Exception.Create('TFormDialog: Out of Range'); Repeating the two operations
ListItems.Items [Index] := Value; above for every component of
end;
every form is certainly boring and
time consuming. To avoid this
➤ Listing 3: The definition of a default array property in a form.
excessive burden, I’ve written a
simple wizard which generates the
procedure TComponent.SetReference(Enable: Boolean); lines of code to add to the program
var in a small window. You’ll need to
Field: ^TComponent;
begin do two simple copy and paste
if FOwner <> nil then begin
Field := FOwner.FieldAddress(FName); operations for each form.
if Field <> nil then
if Enable then The wizard doesn’t automati-
Field^ := Self cally place the source code in the
else
Field^ := nil; proper location: I’m working to fix
end;
end; this and you can check my website
(www.marcocantu.com) for an
updated version.
➤ Listing 4: The VCL code used to hook a component to its reference in
the owner form.
Part 2: Inheritance
After a first set of rules devoted to
Rule 14: Hide Components Delphi uses RTTI and TObject classes, and particularly form
Too often I hear OOP purists com- methods to perform this. classes, here comes another short
plaining because Delphi forms If you want to understand the list of suggestions and tips related
include the list of the components details, refer to Listing 4, which has to inheritance and visual form
in the published section, an the code of the SetReference inheritance.
approach that doesn’t conform to method of the TComponent class,
the principle of encapsulation. which is called by InsertComponent, Rule 16:
They are actually pointing out an RemoveComponent and SetName. Visual Form Inheritance
important issue, but most of them Once you know this, you realise This is a powerful mechanism, if
seem to be unaware that the solu- that by moving the component ref- used properly. From my experi-
tion is at hand without rewriting erences from the published to the ence, its value grows with the size
Delphi or changing the language. private section you lose this auto- of the project. In a complex pro-
The component references matic behaviour. To fix the prob- gram you can use the hierarchical
which Delphi adds to a form can be lem, simply make it manual, by relationship among forms to
moved to the private portion, so adding the following code for each operate on groups of forms with
that they won’t be accessible by component in the OnCreate event polymorphism.
other forms. This way you can handler of the form: Visual form inheritance allows
make compulsory the use of prop- you to share the common behav-
erties mapped to the components Edit1 := FindComponent(‘Edit1’) iour of multiple forms: you can
(see Rule 11 above) to access their as TEdit; have common methods, proper-
status. ties, event handlers, components,
If Delphi places all the compo- The second operation you have to component properties, compo-
nents in the published section, this do is register the component nent event handlers, and so on.
is because of the way these fields classes in the system, so that their
are bound to the components cre- RTTI information is included in the Rule 17: Limit Protected Data
ated from the .DFM file. When you compiled program and made avail- When building a hierarchy of
set a component’s name the VCL able to the system. This is needed classes, some programmers tend
automatically attaches the compo- only once for every component to use mainly protected fields, as
nent object to its reference in the class, and only if you move all the private fields are not accessible by
form. This is possible only if the component references of this type subclasses. I won’t say this is
reference is published, because to the private section. You can add always wrong, but it is certainly

14 The Delphi Magazine Issue 47


against encapsulation. The imple- methods you can call from the the form. Otherwise the initialis-
mentation of protected data is external classes to obtain ation code in the form constructor
shared among all inherited forms, polymorphism. If this is a common (which uses the components) will
and you might have to update all of approach, it is less frequent to see be executed before the OnCreate
them in case the original definition protected virtual methods, called method of the form, which con-
of the data changes. by other public methods. This is an nects the references to the actual
Notice that if you follow the rule important technique, as you can components.
of hiding components (Rule 14) the customise the virtual method in a On the disk you’ll also find the
inherited forms can’t possibly derived class, modifying the compiled package of a first draft
access the private components of behaviour of the objects. version of the OOP Form Wizard,
the base class. In an inherited form, but you should (hopefully) be able
code such as Edit1.Text := ‘’; will Rule 20: Virtual to find a more complete version on
not be compiled anymore. I can see Methods For Properties my website.
this might not be terribly handy, Even property access methods can
but at least in theory it should be be declared as virtual, so that a Conclusion
regarded as a positive thing, not derived class can change the Programming in Delphi according
negative. If you feel this is too behaviour of the property without to good OOP principles is far from
much of a concession to encapsu- having to redefine it. This obvious, as some of the rules I’ve
lation, declare the component ref- approach is seldom used by the listed highlight. I don’t think that
erences in the protected section of VCL but is very flexible and power- you should consider all of my rules
the base form. ful. To accomplish this, simply compulsory, as some of them
declare as virtual the Get and Set might stretch your patience. The
Rule 18: methods of Rule 11. The base form rules should be applied in the
Protected Access Methods will have the code of Listing 5. proper context, and become more
It is much better, instead, to keep In the inherited form you can and more important as the size of
the component references in the now override the virtual method the application grows, along with
private section and add access SetText, to add some extra the number of programmers work-
functions to their properties to the behaviour: ing on it. Even for smaller pro-
base class. If these access func- grams, however, keeping in mind
tions are used only internally and procedure TFormInherit.SetText( the OOP principles underlying my
are not part of the class interface, const Value: String); rules (encapsulation before all
you should declare them as pro- begin others) can really help.
tected. For example, the GetText inherited SetText (Value); There are certainly many other
and SetText form methods if Value = ‘’ then rules of thumb you can come up
described in Rule 11 can become Button1.Enabled := False; with, as I haven’t tried to get into
protected and we could access the end; memory handling and RTTI issues,
edit text by calling: which are so complex to deserve
The Code specific articles.
SetText(‘’); All the code fragments in this arti- My conclusion is that following
cle can be found in the OopDemo the rules I’ve highlighted has a
Actually, as the method was example project, included on this cost, in terms of extra code: it is
mapped to a property, we can month’s disk. You should check in the price you have to pay to obtain
simply write: particular the secondary form (in a more flexible and robust pro-
the frm2 unit) and the derived one gram. It is the price of object ori-
Text := ‘’; (in the inher unit). Notice that in ented programming. Let’s hope
order to use, at the same time, a that future Delphi versions help us
Rule 19: custom constructor with initialis- reduce that price.
Protected Virtual Methods ation code and the private compo-
Another key point to have a flexible nent references, it is necessary to
hierarchy is to declare virtual set the OldCreateOrder property of Marco Cantù is the author of the
Mastering Delphi series, Delphi
Developer’s Handbook, and of
➤ Listing 5: A form with properties implemented with virtual methods.
the free online book Essential
type Pascal. He teaches classes on
TFormDialog = class(TForm) Delphi foundations and advanced
procedure FormCreate(Sender: TObject);
private topics. Check his website at
Edit1: TEdit;
protected www.marcocantu.com for more
function GetText: String; virtual;
procedure SetText(const Value: String); virtual; information. You can reach him
public on his public newsgroups: see the
constructor Create (Text: string); reintroduce; overload;
property Text: String read GetText write SetText; website for details.
end;

July 1999 The Delphi Magazine 15

You might also like