[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