Using interfaces across the hierarchy

Examining a Delphi object

When you create a new project, the IDE displays a new form for you to cus­tomize. In the Code editor, the automatically generated unit declares a new class type for the form and includes the code that creates the new form instance. 33. 3.3 Scope and qualifiers

Scope determines the accessibility of an object's fields, properties, and meth­ods. All members declared in a class are available to that class and, as is discussed later, .often to its descendants. Although a method's implementation code appears out-side of the class declaration, the method is still within the scope of the class because it is declared in the class declaration.

Private, protected, public, and published declarations

The public section declares fields and methods with no access restrictions. Class instances and descendant classes can access these fields and methods. The pro­tected section includes fields and methods with some access restrictions. The private section declares fields and methods that have rigorous access restrictions. A private member is accessible only within the unit where it is declared. A published member has the same visibility as a public member, but the compiler generates runtime type information for published members. Published properties appear in the Object Inspec­tor at design time.

When you declare a field, property, or method, the new member is added to one of these four sections, which gives it its visibility: private, protected, public, or published.

Using object variables

You can assign one object variable to another object variable if the variables are of the same type or are assignment compatible. In particular, you can assign an object variable to another object variable if the type of the variable to which you are assigning is an ancestor of the type of the variable being assigned.

Many of the objects you use in the Form Designer, such as buttons and edit boxes, are visible at both design time and runtime. Some, such as common dialog boxes, appear only at runtime. Still others, such as timers and data source compo­nents, have no visual representation at runtime.

The Create method is called a constructor. It allocates memory for a new in­stance object and returns a reference to the object.

Components on a form are created and destroyed automatically. However, if you write your own code to instantiate objects, you are responsible for disposing of them as well. Every object inherits a Destroy method (called a destructor) from TOb- ject. To destroy an object, however, you should call the Free method (also inherited from TObject), because Free checks for a nil reference before calling Destroy.

Components and ownership

Delphi components have a built-in memory-management mechanism that al­lows one component to assume responsibility for freeing another. The former com­ponent is said to own the latter. The memory for an owned component is automati­cally freed when its owner's memory is freed. The owner of a component the value of its Owner property is determined by a parameter passed to the constructor when the component is created. By default, a form owns all components on it and is in turn owned by the application. Thus, when the application shuts down, the memory for all forms and the components on them is freed.

Defining new classes

Although there are many classes in the object hierarchy, you are likely to need to create additional classes if you are writing object-oriented programs. The classes you write must descend from TObject or one of its descendants.

The advantage of using classes comes from being able to create new classes as descendants of existing ones. Each descendant class inherits the fields and methods of its parent and ancestor classes. You can also declare methods in the new class that override inherited ones, introducing new, more specialized behavior.

To define a class:

1. In the IDE, start with a project open and choose File|New|Unit to create a new unit where you can define the new class.

2. Add the uses clause and type section to the interface section.

3. In the type section, write the class declaration. You need to declare all the member variables, properties, methods, and events.

If you want the class to descend from a specific class, you need to indicate that class in the definition:

TMyClass = class(TPareiitClass); {This descends from TParentClass}

4. Some editions of the IDE include a feature called class completion that sim­plifies the work of defining and implementing new classes by generating skeleton code for the class members you declare. If you have code completion, invoke it to finish the class declaration: place the cursor within a method definition in the inter­face section and press Ctrl+Shift+C (or right-click and select Complete Class at Cur­sor). Any unfinished property declarations are completed, and for any methods that require an implementation, empty methods are added to the implementation section.

If you do not have class completion, you need to write the code yourself, com­pleting property declarations and writing the methods.

5. Fill in the methods. For example, to make it so the button beeps when you call the DoSomething method, add the Beep between begin and end.

Using interfaces

Delphi is a single-inheritance language. That means that any class has only a single direct ancestor. However, there are times you want a new class to inherit prop­erties and methods from more than one base class so that you can use it sometimes like one and sometimes like the other. Interfaces let you achieve something like this effect.

An interface is like a class that contains only abstract methods (methods with no implementation) and a clear definition of their functionality. Interface method definitions include the number and types of their parameters, their return type, and their expected behavior. By convention, interfaces are named according to their be­havior and prefaced with a capital I. For example, an IMalloc interface would allo­cate, free, and manage memory. Similarly, an IPersist interface could be used as a general base interface for descendants, each of which defines specific method proto­types for loading and saving the state of an object to a storage, stream, or file.

Using interfaces across the hierarchy

Using interfaces lets you separate the way a class is used from the way it is im­plemented. Two classes can implement the same interface without descending from the same base class. By obtaining an interface from either class, you can call the same methods without having to know the type of the class. This polymorphic use of the same methods on unrelated objects is possible because the objects implement the same interface.

Interfaces allow you to write generic procedures that can handle objects with­out requiring that the objects descend from a particular base class.