|
Using InterfacesWhen you define an abstract class to represent the base class of a hierarchy, you can come to a point at which the abstract class is so abstract that it only lists a series of virtual functions without providing any implementation. This kind of purely abstract class can also be defined using a specific technique: an interface. For this reason, we refer to these classes as interfaces. Technically, an interface is not a class, although it may resemble one. Interfaces are not classes, because they are considered a totally separate element with distinctive features:
It is important to note that interfaces support a slightly different OOP model than classes. Interfaces provide a less restricted implementation of polymorphism. Object reference polymorphism is based around a specific branch of a hierarchy. Interface polymorphism works across an entire hierarchy. Certainly, interfaces favor encapsulation and provide a looser connection between classes than inheritance. Notice that the most recent OOP languages, from Java to C#, have the notion of interfaces. Here is the syntax of the declaration of an interface (which, by convention, starts with the letter I): type ICanFly = interface ['{EAD9C4B4-E1C5-4CF4-9FA0-3B812C880A21}'] function Fly: string; end; This interface has a GUID (Globally Unique Identifier)—a numeric ID following its declaration and based on Windows conventions. You can generate these identifiers by pressing Ctrl+Shift+G in the Delphi editor. Once you've declared an interface, you can define a class to implement it: type TAirplane = class (TInterfacedObject, ICanFly) function Fly: string; end; The RTL already provides a few base classes to implement the basic behavior required by the IInterface interface. For internal objects, use the TInterfacedObject class I've used in this code. You can implement interface methods with static methods (as in the previous code) or with virtual methods. You can override virtual methods in inherited classes by using the override directive. If you don't use virtual methods, you can still provide a new implementation in an inherited class by redeclaring the interface type in the inherited class, rebinding the interface methods to new versions of the static methods. At first sight, using virtual methods to implement interfaces seems to allow for smoother coding in inherited classes, but both approaches are equally powerful and flexible. However, the use of virtual methods affects code size and memory.
Now that you have defined an implementation of the interface, you can write some code to use an object of this class, through an interface-type variable: var Flyer1: ICanFly; begin Flyer1 := TAirplane.Create; Flyer1.Fly; end; As soon as you assign an object to an interface-type variable, Delphi automatically checks to see whether the object implements that interface, using the as operator. You can explicitly express this operation as follows: Flyer1 := TAirplane.Create as ICanFly; Whether you use the direct assignment or the as statement, Delphi does one extra thing: It calls the _AddRef method of the object (defined by IInterface). The standard implementation of this method, like the one provided by TInterfacedObject, is to increase the object's reference count. At the same time, as soon as the Flyer1 variable goes out of scope, Delphi calls the _ Release method (again part of IInterface). The TInterfacedObject's implementation of _Release decreases the reference count, checks whether the reference count is zero, and, if necessary, destroys the object. For this reason, the previous example doesn't include any code to free the object you've created. In other words, in Delphi, objects referenced by interface variables are reference-counted, and they are automatically de-allocated when no interface variable refers to them any more.
|
|
Copyright © 2004-2024 "Delphi Sources" by BrokenByte Software. Delphi Programming Guide |
|