Extension generalizing inheritance?

Started by
9 comments, last by GameDev.net 17 years, 7 months ago
Inheritance is plainly simple: you take a class (Base), add data and methods on top of it, modify the behaviour of existing methods, and you obtain a new class (Derived). Thus, it is a compile-time decoration of a given type. What about runtime decoration of any subtype of a given type? Extension (this is how I call it, for lack of a better word) takes a class (Base), adds data and methods on top of it, modifies the behaviour of existing method, and provieds a new class (Extended). Except that the extension builds on top of the polymorphic type Base, instead of monomorphic. This allows:
Base b = new Base;
Derived d = new Derived;
Extended e1 = new Extended(b); // extend b
Extended e2 = new Extended(e1); // extend e1
Extended e3 = new Extended(d); // extend d
By all means, any Extended is-a Base (because somewhere, in there, there will be a Base). However, the extension code/data can be added on top of any object which satisfies the Base signature. And besides, you can even add it at runtime (you don't have to build an entire Derived class including its base: you simply add the extension on top of the base class). I'm pretty sure this already exists, has a clear name, has detailed advantages/disadvantages and is implemented threefold in CLOS. Can you give me pointers to that kind of resources?
Advertisement
To me, this sounds a lot like the Decorator design pattern, though honestly, this is one pattern I'm not as familiar with.
Quote:Original post by Rainault
To me, this sounds a lot like the Decorator design pattern, though honestly, this is one pattern I'm not as familiar with.


The main problem I have with the Decorator pattern is that without some unusual contraption a Decorator is-not-a Component (and definitely isn't in the general formulation). This makes decorating a decorated decorator difficult.

Forget I said that.
Quote:
By all means, any Extended is-a Base (because somewhere, in there, there will be a Base). However, the extension code/data can be added on top of any object which satisfies the Base signature. And besides, you can even add it at runtime (you don't have to build an entire Derived class including its base: you simply add the extension on top of the base class).

I'm pretty sure this already exists, has a clear name, has detailed advantages/disadvantages and is implemented threefold in CLOS.
Well, you immediately run into the diamond problem: the extension overrides some methods in the base class. When you're extending a derived class which also overrides one of the methods the extension overrides, which method is applicable?

Is this different from multiple inheritance?

What problem do you want to solve?
Quote:Well, you immediately run into the diamond problem: the extension overrides some methods in the base class. When you're extending a derived class which also overrides one of the methods the extension overrides, which method is applicable?


In your example, the extension method would be called. An extension is, after all, a runtime derived class of whatever type the extended object is, so its methods take precedence (as would happen if the extension had been declared as a derived class at compile time).

Quote:Is this different from multiple inheritance?


Yes. Multiple inheritance creates a type that can have two unrelated supertypes. Extension only allows a single supertype (albeit polymorphic). Conversely, multiple inheritance is performed at compile-time, while extension is done at runtime.

Quote:What problem do you want to solve?


None.
I've used the Decorator pattern a lot in implementing filter chains in C++. It's less useful for that in functional languages, though, since it's basically just a band-aid over C++'s lack of functional features.
I've had the same idea, also to solve no particular problem. Oddly enough.

I think you can get something close (i.e. without the need for manual delegation t o the parent) if your base is a Pimpl and you then inherit from both the base (virtually? privately?) and the base-implementation. That's seriously ugly, though.
Sounds a bit similar to the prototype pattern and prototype-based languages. The CRT pattern (in C++) also comes to mind.
Quote:
In your example, the extension method would be called. An extension is, after all, a runtime derived class of whatever type the extended object is, so its methods take precedence (as would happen if the extension had been declared as a derived class at compile time).
Well, that's not very nice because any specialized behaviour in any derived class method that is overriden is lost by extending it. So you have to be careful that the new extension behaves well in all derived classes you want to extend.

Thankfully, CLOS provides a better way of extending classes by providing :around, :before and :after methods that run (as their name suggests) before, after and around the methods they're put into. So you get to keep the behaviour of the derived class's method while still telling it to do something more.

This actually IS extending, rather than simply overriding as in your original proposal, and can be tremendously useful. I'll give a simple example in an hour or so if you have the patience to wait.
Quote:Original post by Anonymous Poster
Well, that's not very nice because any specialized behaviour in any derived class method that is overriden is lost by extending it. So you have to be careful that the new extension behaves well in all derived classes you want to extend.


You still have access to the extended class from the extending class as a "base" member, just as if you had only inherited from it. The base member has a static type of Base, but its dynamic type can be something else (and calling a polymorphic function on it will use the dynamic type).

Quote:Thankfully, CLOS provides a better way of extending classes by providing :around, :before and :after methods that run (as their name suggests) before, after and around the methods they're put into. So you get to keep the behaviour of the derived class's method while still telling it to do something more.


Using the syntax of my current programming language project (which provides this behavior — procedure overloading uses the runtime type of the argument):

class A {} // The base class class B extends A {} // The "extension"class C extends A {} // The class with "lost" behaviourproc Frobnicate(A _) { print( "A" ); }proc Frobnicate(B b) {  print("This happens before");  Frobnicate (b.base);  print("This happens after");}proc Frobnicate(C _) { print( "C" ); }proc main() {  b = new B { base = new C {} };  Frobnicate(b);}


This would alter the functionality of C by placing some display around the call, resulting in an output of:

This happens before
C
This happens after

This topic is closed to new replies.

Advertisement