Uwe Zdun <uwe.zdun@wu-wien.ac.at> wrote on 08/29/2006
12:20:16 PM:
> well, why do you think the behavior breaks encapsulation?
When I extend Base with BaseMixin, I'm altering the
interface to Base. I can't safely alter the interface to Derived,
because Base and BaseMixin don't necessarily know about Derived. In
fact, since Derived is completely independent, it might not even exist
when BaseMixin is defined. In gerneral BaseMixin can't be defined to work
correctly with all possible derived classes, that set is open.
I believe when I add a mixin to Base I'm intercepting
Base method invocations. When Derived invokes "next", it's logically
chaining to Base. The Base may or may not have BaseMixin, but Derived shouldn't
need to know. With BaseMixin intecepting Derived methods, BaseMixin needs
to be made to work with Base & Derived. In practice, this seems
like you can't safely use a mixin on any class thats also used as a superclass
since the mixin will intercept any derived class's interface.
In practice, it's not possible to work with both.
Assume BaseMixin is intercepting a leaf method with a leaf method: if BaseMixin
invokes 'next' it will break Base and if it doesn't invoke 'next' it will
break Derived.
The current behavior means I can't intercept a subset
of the hierarchy, I have to intercept the whole thing. That means
I can't encapsulate the use of a mixin; the mixin gets applied globally.
> Rather a
> behavior where mixin are not added in precedence before the class
> hierarchy, requires you to know about the interceptor and might break
> encapsulation. Consider you are omitting to use "next" in
a method of
> Derived, for instance simply because this method exists nowhere else
in
> the class hierarchy. If the mixin would be introduced after Derived,
you
> would need to modify Derived to introduce a method of the same name
on
> the mixin. If the mixin is always before the class hierarchy, as a
base
> hierarchy developer you don't need to care for how a potential mixin
is
> designed, because you can be sure once a call has reached your class,
> you can be sure, you cannot accidently introduce side-effects on mixins
> (maybe designed later by other developers).
To be clear, I agree that a mixin should take precedence
over the class it's mixed into. I don't think it should take precedence
over classes derived from the class it's mixed into. If I wanted
to intercept method invocations on Derived, I would add a mixin to Derived
but my explicit semantic is that I want to intercept methods on Base.
To ground this discussion a bit more in reality, my
base class accesses hardware (Device). I then have a AppDevice class
derived from the Device class that I use in my application. AppDevice
will get a method, perform some processing and then invoke Device (via
'next').
So from the app point of view I have:
AppDevice --> Device
For testing purposes I want to use a simulator. I
created a DeviceSim class that reads and writes a buffer instead of the
HW. I don't want my derived classes to know or care if it's using the simulator
or the actual device. In either case the classes define leaf methods;
the methods don't (and can't) chain.
In the test environment I want DeviceSim to intercept
Device methods ("Device mixin add DeviceSim"):
AppDevice --> DeviceSim --> Device
But what I get is:
DeviceSim --> AppDevice --> Device
Since both Device and DeviceSim have leaf methods,
adding DeviceSim as mixin to Device breaks AppDevice. It's the documented
behavior, but it doesn't feel like the right behavior. The behavior
I wanted (and what my code expressed) was to intercept Device methods not
AppDevice methods. I don't see any way to limit interception to the
subset of the hierarchy that I want.
Is what I'm trying to do unreasonable? It feels like
I'm using the language features as intended, I just don't get the desired
result.