[fpc-pascal] "is"

Thomas Schatzl tom_at_work at gmx.at
Thu Apr 7 14:24:53 CEST 2005


Hello,

Michael Van Canneyt schrieb:

> For example;
> MI for interfaces for me is strange (to say the least) because
> interfaces were introduced to avoid the mess of MI in the first
> place. So why on earth would you want to introduce it ??

Because MI itself isn't bad, it's often an appropriate representation of
some abstract model. There's only the problem when there are different
implementations for the same method, that's why it is generally avoided. 
This is multiple _implementation_ inheritance. Commonly considered bad =)

Since interfaces do not have/inherit implementations by definition this
is not an issue, they simply aggregate function *specifications* (no 
code). This is multiple _interface_ inheritance. Commonly considered good =)

Effectively it cuts down typing involved in creating interface
hierarchies dramatically, or actually enabling some things. Another 
thing that comes to my mind is that it allows some clever tagging with 
empty interfaces (without saying anything of importance).

Example (probably not ideal, but I hope mostly self-explaining):

type
   IInput = interface
     ... method specifications ...
   end;
  IOutput = interface
     ... method specifications ...
  end;
  IInOut = interface(Input, Output)
     ... method specifications ...
  end;

  IBlockdeviceReaderWriter = interface(IInout, ...)
     ... method specifications ...
  end;

  IStreamedReaderWriter = interface(IInout, ...)
    ... method specifications ...
  end;

  TSomeClass = class(<ancestorA>, IBlockdeviceReaderWriter)
    ... method specs + implementations ...
  end;

  TSomeOtherClass = class(<ancestorB>, IStreamReaderWriter)
  end;

  [...]

- You can use both TSomeClass and TSomeOtherClass in methods using an 
IInOut now, this includes easy testing that TSomeClass and 
TSomeOtherClass have the required capabilities by

var instance : either TSomeClass or TSomeOtherClass;

SomeProcessingMethod(x : IInout);
   and
SomeProcessingMethod(x as IInout);

Not

SomeProcessingMethod(x : TObject);
   and
SomeProcessingMethod(x);

and test that x implements both IOut and IIn before using it in the 
method (or before calling the method, or using overloading in some cases).

Still both implementations retain the flexibility of being allowed to be 
derived some different ancestors _if needed and appropriate_.

- if you add a subinterface to IInOut you'd have to revise all your code 
and add this interface to all class specifications... I don't hope you 
forget one (since the "as" is evaluated at compile time, happy bug hunting).

- tagging of instances with (empty) interfaces and use of existing 
methods for checking.

type
	IMammal = interface;
	IMeatEater = interface;
	IPlantEater = interface;
	IEatsEverything = interface(IMeatEater, IPlantEater);
	IRat = interface(IMammal, IEatsEverything);

Checking an instance for being a rat (implementing IRat) is just a 
matter of "supports(instance, IRat)", not necessarily 
"supports(instance, IMammal) and supports(instance, IMeatEater) and 
supports(instance, IPlantEater)".

In Pascal sets may be used for that. But consider that every one of 
those interfaces specifies one or more particular methods... (maybe this 
example is better than the above one after all). There's no way other 
than dirty tricks(*1) to make sure that the instance implements a method 
which is called according to the interface type (and actually call it!).

The classes implementing these interface still must implement the 
methods (*once* for every class hierarchy build). But they don't need to 
have a common ancestor. (Using decorators you can employ those though).

*1: my best bet would be defining a common abstract class providing all 
methods in an abstract way, and the successors implementing this one 
partially. From the design pov this is not a good idea...

I hope this somewhat gives you an idea of the capabilites of this feature.

(Actually COM interfaces can be considered somewhat degenerate in that 
aspect)

----------------
		
Btw, also D8 supports it:

http://bdn.borland.com/article/0,1410,29779,00.html

I don't see a reason why there's an ambiguous call error in the example
(I think this is due to implementation restrictions/compatibility
issues) - after all there is only one implementation for ICombo available.

Btw, D8+ also supports "as" between two interfaces as seen from the example.

Regards,
   Thomas






More information about the fpc-pascal mailing list