Defining an interface
The first step in using interfaces is to define an interface. Typically in the interface section of a unit, it is similar to defining a class. The primary differences are that the interface keyword is used instead of the class keyword, and all that can be defined are functions, procedures and properties. No variable or constant declarations are allowed.One thing unique about interfaces is they each should have a unique GUID. This identifier is used to find the interface when given an object or interface reference of a different type. While the GUID is optional, it is strongly recommended that all interfaces have one since its absence will make certain functions quietly fail. This can lead to a bit of head scratching. A GUID can be automatically generated in the IDE by pressing Control-Shift-G.
For example:
type IIterator = interface ['{DFA2FE47-053A-4F5C-BB30-8F2A8C6936EE}'] function AtEnd: Boolean; function NextItem: TObject; end;
Creating an implementation
An interface by itself doesn't do anything. There needs to be a class that implements it. This is done most simply by creating a class that descends from TInterfacedObject and is flagged as implementing the interface. There are additional ways of doing this that will be discussed in another article, but for now we'll use this simple way.The first line of the declaration says we're creating a class of type TListIterator that descends from TInterfacedObject and implements IIterator. It then says we've got two private variables: one will be used to store the current position and the other will store the list object we're iterating over. We have three methods: a constructor that takes the list as a parameter and initializes the two private variables, and implementations of the AtEnd and NextItem functions declared in the interface. All methods declared in an interface need to be implemented somewhere in the class hierarchy at or above the implementing class. Typically, they are implemented in the same class as the one implementing the interface, but they could be implemented in ancestor classes.type TListIterator = class(TInterfacedObject, IIterator) private fList: TList; fPosition: Integer; public constructor Create(const aList: TList); function AtEnd: Boolean; function NextItem: TObject; end; constructor TListIterator.Create(const aList: TList); begin inherited Create; fList := aList; fPosition := 0; end; function TListIterator.AtEnd: Boolean; begin result := fPosition >= fList.Count; end; function TListIterator.NextItem: TObject; begin result := nil; if fPosition < fList.Count then begin result := fList[aPosition]; Inc(fPosition); end; end;
Using the implementation
Here are some methods from a test form. First a method to create a list for testing.Now simply doing some clean-up housekeepping.procedure TfrmInterfaces101.AfterConstruction; var i: Integer; begin inherited; cList := TObjectList.Create; cList.OwnsObjects := True; for i := 1 to 10 do cList.Add(TObject.Create); end;
Now the use of the iterator.procedure TfrmInterfaces101.BeforeDestruction; begin inherited; cList.Free; end;
Just as if normal class types are used, this method first declares a variable of the IIterator type. Next the variable is assigned a new instance of a class. This is where the first difference can be noticed; the object is instantiated with the TListIterator class but it's assigned to a variable of a different type. Objects that implement an interface, either directly or in an ancestor, can be directly assigned to variables of that interface type. Next there's a loop that uses the two methods of the interface to get each item in the list.procedure TfrmInterfaces101.btnTestClick(Sender: TObject); var lIterator: IIterator; begin lIterator := TListIterator.Create(cList); while not lIterator.AtEnd do memoResults.Lines.Add(Format('%p', [Pointer(lIterator.NextItem)])); end;
Finally, notice there's no freeing of the iterator. In general, when objects are assigned to an interface there's no need to free the implementing object. The object is freed when the last interface reference goes out of scope. So, in this case, the TListIterator is freed in the compiler generated code related to the "end" statement.
No comments:
Post a Comment