The previous class uses public data, so for the sake of encapsulation, you should instead change it to use private data and data-access functions. An even better solution is to add a property to the form. Every time you want to make some information of a form available to other forms, you should use a property, for all the reasons discussed in the section "Encapsulating with Properties." To do so, change the field declaration of the form (in the previous code) by adding the keyword property in front of it, and then press Ctrl+Shift+C to activate code completion. Delphi will automatically generate all the extra code you need.
The complete code for this form class is available in the FormProp example and illustrated in Figure 2.3. The program can create multi-instances of the form (that is, multiple objects based on the same form class), each with its own click count.
Figure 2.3: Two forms of the FormProp example at run time
Note Notice that adding a property to a form doesn't add to the list of the form properties in the Object Inspector.
In my opinion, properties should also be used in the form classes to encapsulate the access to the components of a form. For example, if you have a main form with a status bar used to display some information (and with the SimplePanel property set to True) and you want to modify the text from a secondary form, you might be tempted to write
Form1.StatusBar1.SimpleText := 'new text';
This is a standard practice in Delphi, but it's not a good one, because it doesn't provide any encapsulation of the form structure or components. If you have similar code in many places throughout an application, and you later decide to modify the user interface of the form (for example, replacing StatusBar with another control or activating multiple panels), you'll have to fix the code in many places. The alternative is to use a method or, even better, a property to hide the specific control. This property can be defined as
property StatusText: string read GetText write SetText;
with GetText and SetText methods that read from and write to the SimpleText property of the status bar (or the caption of This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks.
one of its panels). In the program's other forms, you can refer to the form's StatusText property; and if the user interface changes, only the setter and getter methods of the property are affected.
Note See Chapter 4 for a detailed discussion of how you can avoid having published form fields for components, which will improve encapsulation. But don't rush there: The description requires a good knowledge of Delphi, and the technique discussed has a few drawbacks.
Constructors
So far, to allocate memory for objects, I've called the Create method. This is a constructor— a special method that you can apply to a class to allocate memory for an instance of that class. The instance is returned by the constructor and can be assigned to a variable for storing the object and using it later. All the data of the new instance is set to zero. If you want your instance data to start out with specific values, then you need to write a custom constructor to do that.
Use the constructor keyword in front of your constructor. Although you can use any name for a constructor, you should stick to the standard name, Create. If you use a name other than Create, the Create constructor of the base TObject class will still be available, but a programmer calling this default constructor might bypass the initialization code you've provided because they don't recognize the name.
By defining a Create constructor with some parameters, you replace the default definition with a new one and make its use compulsory. For example, after you define
type
TDate = class public
constructor Create (y, m, d: Integer);
you'll only be able to call this constructor and not the standard Create: var
The rules for writing constructors for custom components are different, as you'll see in Chapter 9. The reason is that in this case you have to override a virtual constructor. Overloading is particularly relevant for constructors, because you can add multiple constructors to a class and call them all Create; this approach makes the constructors easy to remember and follows a standard path provided by other OOP languages in which constructors must all have the same name. As an example, I've added to the class two separate Create constructors: one with no parameters, which hides the default constructor; and one with initialization values. The constructor with no parameter uses as the default value today's date (as you can see in the complete code of the DataView example):
type
In the same way that a class can have a custom constructor, it can have a custom destructor—a method declared with the destructor keyword and called Destroy. Just as a constructor call allocates memory for the object, a destructor call frees the memory. Destructors are needed only for objects that acquire external resources in their constructors or during their lifetime. You can write custom code for a destructor, generally overriding the default Destroy destructor, to let an object execute some clean-up code before it is destroyed.
Destroy is a virtual destructor of the TObject class. You should never define a different destructor, because objects are usually destroyed by calling the Free method, and this method calls the Destroy virtual destructor of the specific class (virtual methods will be discussed later in this chapter).
Free is a method of the TObject class, inherited by all other classes. The Free method basically checks whether the current object (Self) is not nil before calling the Destroy virtual destructor. Free doesn't set the object to nil
automatically; this is something you should do yourself! The object doesn't know which variables may be referring to it, This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks.
so it has no way to set them all to nil.
Delphi 5 introduced a FreeAndNil procedure you can use to free an object and set its reference to nil at the same time.
Call FreeAndNil(Obj1) instead of writing the following:
Obj1.Free;
Obj1 := nil;
Note There's more on this topic in the section "Destroying Objects Only Once" later in this chapter.