Title: When use Interfaces, when use Inheritance ?
Question: There are two possibilities to define a (same) class hierarchy:
- with interfaces
- with inheritance
Which one suits your needs
Answer:
You can fulfill the same operations with interfaces or inheritance as the following shows:
IShape = interface
procedure paint;
end;
TSquare = class(TInterfacedObject, IShape)
procedure paint;
end;
TCircle = class(TInterfacedObject, IShape)
procedure paint;
end;
TShape = class
procedure paint; virtual; abstract;
//procedure makeShape(afigure: TShape);
end;
TSquare2 = class(TShape)
procedure paint; override;
end;
TCircle2 = class(TShape)
procedure paint; override;
end;
Interfaces are useful when a set of operations, such as rendering or streaming, are used in a broad range of objects. They can reuse code and apply methods to a variety of different applications.
Almost the same could have been accomplished by having TSquare2 and TCircle2 descend from TShape which implemented the virtual method Paint.
So whats the difference from a point of design?
With inheritance you can implement a base behaviour in the base-class like makeShape(), interfaces are pure abstract and dont't allow a real method.
You have garbage collection with interfaces and you can handle objects without having to require the object to descend from a particular base class.
Even if two classes did not share a commen ancestor, they are assignment compatible with a variabel of IShape:
procedure TfrmGen.Button2Click(Sender: TObject);
var painter: IShape;
painter2: TShape;
begin
// interface
painter:= TSquare.create;
painter.paint;
painter:= TCircle.create;
painter.paint;
// inheritance virtual
painter2:= TSquare2.create;
//painter2.paint;
painter2.makeShape(painter2);
painter2:= TCircle2.create;
painter2.makeShape(painter2);
// virtual alternative
with painter2.Create do begin
makeShape(TSquare2.create);
makeShape(TCircle2.create);
end;
end;
A well designed inheritance can be more stable and maintainable in comparison to much runtime objects that implement the same interface. So inheritance has more advantage in a well established design-time hierarchy, interfaces are best in run-time between components.
For example we improve a method in a base class, inheritance makes it possible. On the other hand interfaces are more flexible to replace or delegate objects at runtime.
For this it's a must do generate a GUID (Globally Unique Identifier) in square brackets. GUID's arent strictly necessary, but if you want to switch between interfaces you'll need them to make QueryInterface work!
Now let's compare the advantages between the two
Inheritance Interface
- big hierarchy - delegation
- base behavior - more implemantations of one interface
- libraries - run time packages
- real time freeing - garbage collection (reference counter)
- subclassing - run time flexibility
- design time properties - design by contract
- fields - properties
The most part is the realisation that you cannot mix object references and interface references. Interfaces are reference counted and objects not, so mixing the two approaches gets an access violation.
See you at EKON7 in Frankfurt or in my new book "Patterns konkret" ;)
// example implementation
{ TShape }
procedure TShape.makeShape(afigure: TShape);
begin
if afigure NIL then
afigure.paint;
end;
{ TSquare2 }
procedure TSquare2.paint;
begin
frmgen.memo1.lines.add('square virtual painted');
end;
{ TCircle2 }
procedure TCircle2.paint;
begin
frmgen.memo1.lines.add('circle virtual painted');
end;
end.