2005. január 16., vasárnap

When use Interfaces, when use Inheritance ?


Problem/Question/Abstract:

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.

Nincsenek megjegyzések:

Megjegyzés küldése