Java Forum / General / February 2008
Interface inheritance vs Implementation inheritance.
Daniel Pitts - 19 Feb 2008 18:08 GMT (x-posted to c.object and c.l.java.programmer) First, a quick bit about my background; most of my OO experience comes from Java, with some C++ in my past. Recently in comp.object, there was a thread "Inheritance of implementation: what is reason of". While the subject line could use some grammatic tweaking, the point was eventually made clear.
I was thinking about it, and it seems to me that implementation inheritance is a convenience that leads to bad design and mistakes. By convenience, I mean that often a programmer will design the an interface and the implementation together in one source unit (e.g. a class). Alternatively, you can define a separate interface (lets call it IFoo) in one source unit, and an implementation (FooImpl). FooImpl is *also* an interface in many languages, it just happens to have the associated implementation.
So far, not that big of a deal. The convolution comes when you add in access modifiers. For instance, in Java you can mark methods/fields as private, protected, package-private, or public. Now, you have up to 4 interfaces to the implementation. Only certain code units can see some of the interfaces, but managing that contract becomes troublesome, especially for the "protected" interface, as many programmers don't design their classes for inheritance but the class gets used that way.
To me, polymorphic behavior is important, and "implementation sharing" is a very useful convenience. I think that they are often co-mingled concepts that should be separated. I have a few ideas on how I might do this, at the language level, but I wanted to see if my above thoughts on the subject stirred any interesting discussions.
My main point is that classes should use composition to gain behavior, possibly with an easy mechanism for delegation. The *only* thing that should be automatically inherited should be the interface. This eliminates the need for (and usefulness of) the "protected" access modifier.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
andrew queisser - 19 Feb 2008 19:03 GMT > (x-posted to c.object and c.l.java.programmer) > [quoted text clipped - 3 lines] > eliminates the need for (and usefulness of) the "protected" access > modifier. In C++ there are various ways implementation inheritance is useful in ways other than inheritance of default implementation, e.g. STL, ATL, WTL, all of which use some form of implementation inheritance to achieve efficiency.
I think the primary problem people are concerned about is implementation of inheritance for virtual functions. Perhaps this distinction is not meaningful outside of the C++ world.
Andrew
Peter Duniho - 19 Feb 2008 19:10 GMT > [...] > My main point is that classes should use composition to gain behavior, > possibly with an easy mechanism for delegation. The *only* thing that > should be automatically inherited should be the interface. This > eliminates the need for (and usefulness of) the "protected" access > modifier. I have a sense that in spite of asking the question, you may have already decided on the answer. I don't intend to get sucked too deep into this conversation. Especially given the somewhat dubious cross-posting.
That said, I disagree. I do agree that inheriting an implementation and implementing an interface are not the same thing, but I disagree that only the latter is a viable approach. Both are important and useful approaches in OOP. Interfaces are good for when there's a specific abstract contract that any object ought to be able to fulfill. Inheriting implementation is good for when an object really "is" a more-specific version of some existing object. And of course composition is also useful, when your object isn't really a more-specific version of some existing object but you still need to take advantage of an existing implementation of an object.
In Java, why should a new class that inherits Container have to reimplement all of the Container class's containment interface? If all you want to do is provide a Container with some specific visual behavior, why should you also have to reimplement all of the non-visual containment behavior when there's a working, useful class that already does all that?
Likewise, abstract classes often (in fact, usually IME, otherwise they'd be interfaces) provide _some_ functionality that only needs one or a few actual abstract members to be implemented in order to get the full functionality of the abstract class. For example, InputStream in Java where you only have to implement "int read()" in a derived class in order for the other two InputStream methods that read data to work. There are often performance advantages to overriding those other two methods as well, but if that's not a design goal why should the implementor of the derived class be forced to reimplement them anyway (especially given that they are very likely to just reimplement them exactly as the abstract class has implemented them, except possibly by reintroducing all the bugs that the original implementor already found and fixed in their implementation).
Is inheritance overused? I'd agree that it is. Programmers often inherit when they should either composite or implement an interface instead. But I disagree that that somehow means that inheriting an implementation is a priori bad. There is still plenty of good examples of useful and proper inheritance of an implementation.
Pete
Daniel Pitts - 19 Feb 2008 21:27 GMT >> [...] >> My main point is that classes should use composition to gain behavior, [quoted text clipped - 7 lines] > into this conversation. Especially given the somewhat dubious > cross-posting. I only cross-posted to cljp because I'm regular there, and appreciate their input.
> That said, I disagree. I do agree that inheriting an implementation and > implementing an interface are not the same thing, but I disagree that [quoted text clipped - 13 lines] > containment behavior when there's a working, useful class that already > does all that? That is a perfectly valid point. My thoughts are that it should be *easy* to "borrow" implementation from another object, without using inheritance but instead using composition. To borrow your Container example: Say you have MyContainer which implements the Container interface, and delegates (with as clean a syntax as possible) most of its behavior to another Container instance. You don't have to re-implement the containment behavior.
> Likewise, abstract classes often (in fact, usually IME, otherwise they'd > be interfaces) provide _some_ functionality that only needs one or a few [quoted text clipped - 9 lines] > reintroducing all the bugs that the original implementor already found > and fixed in their implementation). I think what you just described is related to (but not exactly) the Template design pattern. I think that the problem solved by the Template pattern can be solved using a callback pattern instead.
> Is inheritance overused? I'd agree that it is. Programmers often > inherit when they should either composite or implement an interface > instead. But I disagree that that somehow means that inheriting an > implementation is a priori bad. There is still plenty of good examples > of useful and proper inheritance of an implementation. I'm not saying that it is always and automatically bad. I guess my main point was that there are situations where, due to many language limitations, it is necessary to avoid a massive amounts of boiler-plate delegation code. If you had a convenient way (say one or two source lines) to "borrow" all/most implementation from another class, would you still feel that implementation inheritance is still necessary?
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Peter Duniho - 19 Feb 2008 21:43 GMT > [...] > I'm not saying that it is always and automatically bad. I guess my main [quoted text clipped - 3 lines] > lines) to "borrow" all/most implementation from another class, would you > still feel that implementation inheritance is still necessary? I guess I can only at this point answer "it depends". It seems to me that the current, widely-used implementation inheritance syntax is pretty much just that: a convenient way to borrow implementation from another class.
I would have to see what this alternative syntax would look like to answer more definitively, but it's not clear to me that it'd be any different in practice from simply inheriting an implementation directly (assuming that, as with interfaces, a "borrowing" class still tests "true" for whether it "is" one of these "borrowed" things...if that's not the case, then I'd say that's a glaring omission of the proposal).
Or, looking at the question from another angle, if it's okay to "borrow" implementation from another class, what does it matter whether it was borrowed in the form of inheritance or in the form of a new type of composition that looks just like inheritance?
I'm reading this in the Java newsgroup, and perhaps the "comp.object" crowd has a more thorough academic understanding of OOP practices and principles than I do. But I admit, based on what you've written so far I'm not really clear on the distinction between the way things are now and what you're suggesting they could be, at least from a practical standpoint.
Pete
Patricia Shanahan - 19 Feb 2008 22:05 GMT ...
> Or, looking at the question from another angle, if it's okay to "borrow" > implementation from another class, what does it matter whether it was > borrowed in the form of inheritance or in the form of a new type of > composition that looks just like inheritance? ...
I think the big question is what gets exposed to users of the borrowing class.
To make this a bit more specific, consider ArrayList. It implements a series of interfaces. Those describe the general behavior its callers can depend on: it's a list, it has fast random access, it can be cloned and serialized, and it has an Iterator.
In addition, it extends AbstractList. That seems to me to be part of its implementation, and not something I would use in my code. And yet, a class outside java.util could declare:
AbstractList<String> someList = new ArrayList<String>();
It might be advantageous to split AbstractList, for example to provide different implementations depending on whether the list has fast random access. That cannot be done, because of the public nature of the uses of AbstractList.
Patricia
Lasse Reichstein Nielsen - 20 Feb 2008 06:15 GMT > To make this a bit more specific, consider ArrayList. It implements a > series of interfaces. Those describe the general behavior its callers [quoted text clipped - 11 lines] > access. That cannot be done, because of the public nature of the uses of > AbstractList. Agreed. I wouldn't mind a language where implementation inheritance wasn't public, i.e., your type only consists of interfaces. If you extend another class, you also implements its interfaces, but the extended class is not part of your type.
And I would also like an easy way to create a composition, e.g.,
class Foo implements Bar(b) { private Bar barbarbar implements Bar; // or delegates Bar public Foo(int i) { super(i); this.barbarbar = new Bar(i); } //... }
/L
 Signature Lasse Reichstein Nielsen - lrn@hotpop.com DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html> 'Faith without judgement merely degrades the spirit divine.'
Robert Martin - 20 Feb 2008 13:03 GMT > I wouldn't mind a language where implementation inheritance wasn't public, > i.e., your type only consists of interfaces. If you extend another class, > you also implements its interfaces, but the extended class is not part > of your type. You mean like in C++?
class MyContainer : private SomeContainer { ... }
> And I would also like an easy way to create a composition, e.g., > [quoted text clipped - 6 lines] > //... > } If you think about that a bit longer, you'll realize that an easy way to create composition IS inheritance. Or rather, inheritance is an easy way to create composition.
Every method of the component gets redeclared in the composer. When those methods are called on the composer, the component's implementation is invoked.
Now change the words composer to base, and component to derivative and you'll see the sentence is true.
Perhaps what you are after is a way to make sure that the composer cannot be cast (explicitly or implicitly) to the component. That's what private inheritance gives you.
I conclude from this that we'd all be happy if Java had multiple inheritance and private inheritance.
;-)
 Signature Robert C. Martin (Uncle Bob) | email: unclebob@objectmentor.com Object Mentor Inc. | blog: www.butunclebob.com The Agile Transition Experts | web: www.objectmentor.com 800-338-6716 |
Silvio Bierman - 20 Feb 2008 14:13 GMT > If you think about that a bit longer, you'll realize that an easy way to > create composition IS inheritance. Or rather, inheritance is an easy [quoted text clipped - 15 lines] > > ;-) I disagree. Inheritance and interfaces a la Java have one important distinction: interfaces introduce no implicit implementation of methods. Since Java does not support multiple inheritance implementation inheritance is too limited for many use cases. C++ supports MI but in a very disturbed way (although I was quite happy with it when it was introduced into the language I have learned to really hate it). The issues of virtual bases and multiple down-cast paths leading to different implementations of the same function signature are painful. It is unclear how any implementation of multiple implementation inheritance could circumvent these issues.
The advantage of the Java interface concept is that a class implementing an interface (and not extending any other class than Object) will have to explicitly define how the interface methods are implemented. If it extends multiple interfaces with shared function signatures the seaming ambiguity is resolved at the class level itself.
Unfortunately Java lacks a simple way of expressing interface delegation. I would have loved something like:
interface I1 { public void f1(); public void f2(); public void f3(); }
class C1 implements I1 { public void f1() { /*...*/ } public void f2() { /*...*/ } public void f3() { /*...*/ } }
class C2 implements I1, I2 { private C1 c1a; private C1 c1b; public I1() { return c1a != null ? c1a : c1b; } /* NOT SUPPORTED */ public void f2() { /* THIS IS AN OVERRULE IMPLEMENTATION */ } }
where the constructor-like syntax of the 'public I1() { /*...*/}' notation implies that all methods declared in I1 and not declared in C2 or I2 are implicitly implemented such that given a C2 reference c2 calling c2.f1() is equal to c2.I1().f2().
This syntax would be ideal for implementing partial delegations of interfaces with many methods when writing wrapper classes (the JDBC or Servlet interfaces anyone?).
Would such syntax/semantics exist I would be happy to drop implementation inheritance in most places.
Kind regards,
Silvio Bierman
Robert Martin - 20 Feb 2008 12:56 GMT > I think the big question is what gets exposed to users of the borrowing > class. [quoted text clipped - 14 lines] > access. That cannot be done, because of the public nature of the uses of > AbstractList. This is the reason that C++ has private inheritance. A privately inherited base class is inaccessible to any users or further subclasses.
 Signature Robert C. Martin (Uncle Bob) | email: unclebob@objectmentor.com Object Mentor Inc. | blog: www.butunclebob.com The Agile Transition Experts | web: www.objectmentor.com 800-338-6716 |
Daniel Pitts - 19 Feb 2008 22:10 GMT >> [...] >> I'm not saying that it is always and automatically bad. I guess my [quoted text clipped - 9 lines] > pretty much just that: a convenient way to borrow implementation from > another class. Currently, inheritance of classes "borrows" both the implementation *and* interface. The thing is that interface and implementation are borrowed together, and you have to take extra steps if you only want the implementation or only the interface. I think it might be good to separate those concepts.
> I would have to see what this alternative syntax would look like to > answer more definitively, but it's not clear to me that it'd be any > different in practice from simply inheriting an implementation directly > (assuming that, as with interfaces, a "borrowing" class still tests > "true" for whether it "is" one of these "borrowed" things...if that's > not the case, then I'd say that's a glaring omission of the proposal). If the interface "lives" separately from the implementation, then the borrow can choose to also borrow the interface (or some parent interface, or some composite interface).
> Or, looking at the question from another angle, if it's okay to "borrow" > implementation from another class, what does it matter whether it was > borrowed in the form of inheritance or in the form of a new type of > composition that looks just like inheritance? Because the borrowing can be more pick-and-choose in the new way, instead of the all-or-nothing of inheritance. Also, there are times when your interface maps one-to-one to another class for a few methods. It could be easier to pick out those methods.
> I'm reading this in the Java newsgroup, and perhaps the "comp.object" > crowd has a more thorough academic understanding of OOP practices and > principles than I do. But I admit, based on what you've written so far > I'm not really clear on the distinction between the way things are now > and what you're suggesting they could be, at least from a practical > standpoint. I guess the main difference is the syntax, but I believe it adds flexibility too.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Daniele Futtorovic - 19 Feb 2008 23:58 GMT >>> [...] I'm not saying that it is always and automatically bad. I >>> guess my main point was that there are situations where, due to [quoted text clipped - 42 lines] > I guess the main difference is the syntax, but I believe it adds > flexibility too. /* The following deliberately sets itself strictly within the context of the Java Programming Language, as it often most beneficial to have a practical reference to focus the discussion. */
It seems to me all practical examples you gave in this thread are feasible with the current grammar of Java. There are two discrete models, one for "borrowing" an interface and one for "borrowing" an implementation; the first one is called implementing an interface and the second one extending a class. The practice of always providing both for all classes isn't ubiquitous, or even widespread. But, while I grant this may cause you much grief on diverse occasions, it is simply not an argument against the grammar of Java, but only potentially against some of its uses. On the subject of "borrowing an implementation", let me point out that getting rid of protected modifiers, which you seem to aim at, would effectively _force_ upon you _all_ the implementation that isn't public -- so much for "borrowing can be more pick-and-choose".
The use of composition in a given class is clearly a good idea and obviously superior to inheritance simply in that you can only inherit once, but can compose many times. There are various stances one can take towards composing, from aggressive encapsulating of any functionality that lurks its head to lazily refactoring en masse. Note, however, that composition does not replace, or indeed affect, inheritance essentially: it merely replaces it in the context of one given class. Talking of composition merely _shifts_ the question of inheritance towards the classes actually implementing the encapsulated functionality. Now you may argue that, since through composition you grant the API user means to replace functionality in transparent manner, you can keep all your implementations of the encapsulated functionality locked tight. This would surely be a valid choice, and it may for any given case be justified or not -- but one thing's for sure and that's that you would be giving your customers less for their money by prohibiting re-use of code (unless you make it public throughout, which I don't suppose you'd advocate).
You've been occasionally talking about "clean syntax", "few lines of code", suggesting your aim was at least partially at some kind of syntactic sugar. So let me state this: Java boilerplate code is GREAT. And in my view *any* request for modification of the JLS, the sole argument in favour of which is that it saves typing, does not deserve attention (nota bene: shorter syntax is not the same thing as greater clarity). What in my opinion is one of the greatest things about Java is that, at the basic level, the language features very few denizens: objects, primitives, classes, interfaces, functions, fields -- that's about it. With these basic entities, such high-level concepts as callbacks, closures or concurrent APIs can be built. Entirely transparently so. If someone wishes to see how it's done, they can, by simply reading the source code (except for native methods). They can reproduce it freely. No behind-the-scenes voodoo. Yes, that's it in two words: no voodoo.
At least that's what it was like until they introduced the enhanced for-loop back in Tiger (I'm not entirely sure it was the first occurrence, but it's just an example, don't quote me on that). And I can only hope they won't get through with the closures stuff they're planning for 1.7. This to me is clearly a step in the wrong direction, because it /hides/ what's going on. It's voodoo. The enhanced for-loop takes an Iterable, initialises an iterator, and repeatedly calls next() while checking hasNext(). We know that because it's written in the specification. But the point is that nowhere can we see that in plain Java code anymore. Now don't get me wrong: the enhanced for-loop is really quite a pleasure to use, and even I, who am reluctant to it, have found myself making increasing use of it. But when it comes down to it and I am confronted with the choice between a short syntax where it's unclear what's going on and a longer syntax where you see precisely what's going on -- I'm always going to side with the latter. When you know what you're doing you can always assess that each bit of effort you provide, no matter how much of it is overall necessary, is productive; while in the opposite case you cannot tell whether it may be unproductive or, even worse, counter-productive.
df.
Daniel Pitts - 20 Feb 2008 00:35 GMT >>>> [...] I'm not saying that it is always and automatically bad. I >>>> guess my main point was that there are situations where, due to [quoted text clipped - 60 lines] > effectively _force_ upon you _all_ the implementation that isn't public > -- so much for "borrowing can be more pick-and-choose". Anything that was "protected" is still a "public interface" to the inheritors. You can separate that out quite easily in all the circumstances I can think of.
> The use of composition in a given class is clearly a good idea and > obviously superior to inheritance simply in that you can only inherit [quoted text clipped - 13 lines] > code (unless you make it public throughout, which I don't suppose you'd > advocate). Since the "protected interface" is specific to an implementation, it should be called out differently IMO. As far as re-use goes, its important to limit re-use to things you expect to maintain in the future. You can still add the hooks to have extensibility in your code, enabling re-use. You just use a different pattern.
> You've been occasionally talking about "clean syntax", "few lines of > code", suggesting your aim was at least partially at some kind of [quoted text clipped - 3 lines] > attention (nota bene: shorter syntax is not the same thing as greater > clarity). I agree short/clever != clear, and this is not in the context of Java, despite the fact that I posted to cljp :-) The syntax would be explicit and compact. Having not thought greatly into it. "MyFoo extends Foo {}" might become "MyFoo implements Foo { Foo foo; delegate "*" to foo; }
Yes, its a little more typing, but you can replace that "*" with more specific names, etc... Thats where you get the pick-and-choose. It also allows inversion-of-control in the inheritance structure. Think "I want a MyFoo that extends FooBar, and I want another MyFoo that extends FooLish".
> What in my opinion is one of the greatest things about Java is that, at > the basic level, the language features very few denizens: objects, [quoted text clipped - 4 lines] > source code (except for native methods). They can reproduce it freely. > No behind-the-scenes voodoo. Yes, that's it in two words: no voodoo. I agree with that. I'm not suggesting modifying Java, it would have to be another (possibly not-yet-existent) language.
> At least that's what it was like until they introduced the enhanced > for-loop back in Tiger (I'm not entirely sure it was the first [quoted text clipped - 16 lines] > case you cannot tell whether it may be unproductive or, even worse, > counter-productive. True, and hopefully the "example" syntax I use above helps to show that I'm actually exposing more information rather than less. The "delegate" keyword would expose exactly which methods (by name or whatever) are being exposed.
> df. Thanks (everyone) for the feedback so far. Even if it appear(s/ed) that I already made up my mind, it is something that I'm working out for myself, but want feedback to help confirm or deny my suspicions.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Patricia Shanahan - 20 Feb 2008 01:47 GMT ...
> With these basic entities, such high-level concepts as callbacks, > closures or concurrent APIs can be built. Entirely transparently so. If > someone wishes to see how it's done, they can, by simply reading the > source code (except for native methods). They can reproduce it freely. > No behind-the-scenes voodoo. Yes, that's it in two words: no voodoo. I've heard the "no voodoo" argument many times before, initially from people telling me why assembly languages were better than Fortran or Algol. Later, procedural languages were better than OO, because you can see exactly what function will be invoked, no virtual call voodoo.
The increasing semantic gap between what the programmer writes and what the processor does has been a major theme of programming language development. The effect has been to greatly increase the effectiveness of programmers by automating a lot of details.
What's different this time? Does Java really have *exactly* the right amount of voodoo, so that adding any more will take it past the current sweet spot?
> At least that's what it was like until they introduced the enhanced > for-loop back in Tiger (I'm not entirely sure it was the first > occurrence, but it's just an example, don't quote me on that). I like the enhanced for-loop. If I just want to do something to every element in a collection an enhanced for-loop expresses my intent more directly and clearly than an Iterator idiom.
Patricia
Daniele Futtorovic - 20 Feb 2008 18:13 GMT > Daniele Futtorovic wrote: .... >> With these basic entities, such high-level concepts as callbacks, [quoted text clipped - 18 lines] > amount of voodoo, so that adding any more will take it past the > current sweet spot? You're right, so maybe the emphasis shouldn't be on /how much/ voodoo, but on /how/ voodoo. The point would be to find a minimal set of building blocks, of basic entities, which encapsulate the voodoo, and to keep the set minimal. Especially not to inflate it with synonyms which make no semantical addition.
>> At least that's what it was like until they introduced the enhanced >> for-loop back in Tiger (I'm not entirely sure it was the first [quoted text clipped - 3 lines] > element in a collection an enhanced for-loop expresses my intent > more directly and clearly than an Iterator idiom. Not functionally so, for functionally, you are using an Iterator. What's happened is that, from the point of view of the code, you've introduced a new entity which, though linguistically distinct, makes no semantic addition. It's a synonym.
df.
Patricia Shanahan - 20 Feb 2008 18:33 GMT ...
>> What's different this time? Does Java really have *exactly* the right >> amount of voodoo, so that adding any more will take it past the [quoted text clipped - 5 lines] > keep the set minimal. Especially not to inflate it with synonyms which > make no semantical addition. All you really need is a Turing machine. Its basic entities are a state machine, a current state, and a tape with a single read/write head. Or, to be a bit more practical, consider the original MIPS instruction set. Its assembly language is very simple, with few basic entities.
However, I find it far easier to express my programs in Java than in any assembly language.
>> I like the enhanced for-loop. If I just want to do something to every >> element in a collection an enhanced for-loop expresses my intent [quoted text clipped - 4 lines] > a new entity which, though linguistically distinct, makes no semantic > addition. It's a synonym. Sure. Even assembly languages add new entities which, though linguistically distinct, make no semantic addition. For example, "nop" is often provided as a mnemonic, but mapped to some existing operation that does nothing. What's wrong with that, as long as it makes the resulting programs a clearer expression of the programmer's intent?
Patricia
Robert Martin - 20 Feb 2008 12:51 GMT > My thoughts are that it should be *easy* to "borrow" implementation > from another object, without using inheritance but instead using [quoted text clipped - 3 lines] > Container instance. You don't have to re-implement the containment > behavior. True, but you DO have to write all the delegating functions. Ick. Why should I have to write a bunch of brain-dead delegation functions every time I want to reuse some nice piece of implementation.
It would be trivial to add language feature that did composition-and-delegation for you. It might look like this:
class MyContainer composes SomeContainer { ... }
This would create a hidden instance variable for SomeContainer, and automatically write delegation functions in MyContainer.
Indeed, you might be able to compose more than one class. Let's say you wanted to have a contaier that was observable:
class MyContainer composes SomeContainer, Subject { ... }
How cool would this be! Automatic reuse through composition!!!
There is a name for this feature.
It's called Multiple Inheritance.
 Signature Robert C. Martin (Uncle Bob) | email: unclebob@objectmentor.com Object Mentor Inc. | blog: www.butunclebob.com The Agile Transition Experts | web: www.objectmentor.com 800-338-6716 |
Mike Schilling - 21 Feb 2008 00:50 GMT > True, but you DO have to write all the delegating functions. Ick. > Why should I have to write a bunch of brain-dead delegation [quoted text clipped - 10 lines] > This would create a hidden instance variable for SomeContainer, and > automatically write delegation functions in MyContainer. It would also be trivial for an IDE to generate the delegation methods for you, and no languages changes are required for that.
S Perryman - 21 Feb 2008 12:06 GMT >>True, but you DO have to write all the delegating functions. Ick. >>Why should I have to write a bunch of brain-dead delegation >>functions >>every time I want to reuse some nice piece of implementation.
>>It would be trivial to add language feature that did >>composition-and-delegation for you. It might look like this:
>>class MyContainer composes SomeContainer { >> ... >>}
>>This would create a hidden instance variable for SomeContainer, and >>automatically write delegation functions in MyContainer.
> It would also be trivial for an IDE to generate the delegation methods > for you, and no languages changes are required for that. The big problem with delegation is correctness (safety specifically) :
type T { Delegate D ;
op() ; }
T::op() = D.op() ;
Delegating service execution to some other object(s) D, would require that D cannot be messed with (re-assigned, deleted etc) .
With heap-based object allocation, such guarantees become difficult. With prog langs that can 'embed' a D into the representation of T (C++ , the "expanded" concept in Eiffel etc) , delegation is safer.
Regards, Steven Perryman
Silvio Bierman - 21 Feb 2008 12:11 GMT >> True, but you DO have to write all the delegating functions. Ick. >> Why should I have to write a bunch of brain-dead delegation [quoted text clipped - 13 lines] > It would also be trivial for an IDE to generate the delegation methods > for you, and no languages changes are required for that. IDEs should only generate code to save you some typing, like wrapping code in a try/catch block. Forwarding interface methods to an implementing object is not about typing. It becomes nasty when you have no control over the interfaces (JDBC/Servlet API wrappers are a good example as I mentioned somewhere in this thread) and their development over time. If I want to write a wrapper around such an interface I may just have to implement two or three methods and simply forward the rest.
Having the IDE generate my "forward the rest" code will result in a class that only works with a specific version of the interface. If you own the interface this is already a maintenance nightmare requiring very advanced refactoring support to be manageable but if it is a public interface that evolves (usually this means new methods are added in time) your code gets broken in time automatically.
IDEs generating code have very often been an excuse to create horrendous constructs. Just look at the EJB specs for examples. Microsoft's MFC was a prime candidate as well.
Regards,
Silvio Bierman
Mike Schilling - 21 Feb 2008 15:14 GMT >>> True, but you DO have to write all the delegating functions. Ick. >>> Why should I have to write a bunch of brain-dead delegation [quoted text clipped - 31 lines] > methods are added in time) your code gets broken in time > automatically. That's true of any interface that adds methods. Classes that formerly implemented it successfully will fail to compile.
Lew - 21 Feb 2008 15:28 GMT > "Silvio Bierman" <sbierman@jambo-software.com> wrote in message >> Having the IDE generate my "forward the rest" code will result in a [quoted text clipped - 7 lines] > That's true of any interface that adds methods. Classes that formerly > implemented it successfully will fail to compile. Ditto with abstract classes and new abstract methods.
 Signature Lew
Mike Schilling - 21 Feb 2008 16:26 GMT >> "Silvio Bierman" <sbierman@jambo-software.com> wrote in message >>> Having the IDE generate my "forward the rest" code will result in [quoted text clipped - 7 lines] >> That's true of any interface that adds methods. Classes that >> formerly implemented it successfully will fail to compile. And given how the JVM and bytecode work, it would, unless I'm missing something, be equally true of a Java language feature that performed delegation. That is, the feature would generate bytecode to do the forwarding (possibly being clever enough not to generate forwarding methods for methods that are hand-coded), not adjust its beahcior at run-time according to the version of the interface currently loaded.
> Ditto with abstract classes and new abstract methods. Yup. One of the advantages of defining an abstract class that clients can extend vs.defining an interface for them to implement, is that you can later add new methods by giving them default implementations. Obviously this won't always work, and there are disadvantages to abstract classes as well, but it's something to consider when designing a framework.
Silvio Bierman - 26 Feb 2008 22:40 GMT > And given how the JVM and bytecode work, it would, unless I'm missing > something, be equally true of a Java language feature that performed [quoted text clipped - 11 lines] > abstract classes as well, but it's something to consider when > designing a framework. I am aware that a compiler trick is not enough to implement such a feature. It would require auto-delegation logic at the JVM level which makes it unlikely to ever happen. Looking at the hoops they jumped through to sort-of implement generics without proper JVM support does not give me much hope.
Regards,
Silvio
Mike Schilling - 27 Feb 2008 01:37 GMT >> And given how the JVM and bytecode work, it would, unless I'm >> missing [quoted text clipped - 23 lines] > does > not give me much hope. You misunderstand. What you see are the hoops they jumped through to add generics without introducing either source or binary incompatibility when a non-generic class is converted to a generic one. You can argue about how important this goal was, but I've never seen a superior solution to it.
> Regards, > > Silvio Roger Lindsjö - 21 Feb 2008 18:42 GMT > Having the IDE generate my "forward the rest" code will result in a > class that only works with a specific version of the interface. If you [quoted text clipped - 6 lines] > constructs. Just look at the EJB specs for examples. Microsoft's MFC was > a prime candidate as well. In that case you could use a build a Proxy which just intercepts the methods you recognize.
However, trying to be "forward compatible" could still be a mess as not only might the interface have new methods added to it, but the existing methods could change their meaning which would probably make the wrapping code invalid anyway.
 Signature Roger Lindsjö
Robert Martin - 20 Feb 2008 12:44 GMT > My main point is that classes should use composition to gain behavior, > possibly with an easy mechanism for delegation. The *only* thing that > should be automatically inherited should be the interface. This > eliminates the need for (and usefulness of) the "protected" access modifier. Polymorphism is a good thing, and the use of interfaces to achieve polymorphism should be encouraged in Java. On the other hand, there are times when inheritance of implementation is -- pleasant.
The standard Observer is a common enough example. Observable classes need to be able to register and notify observers. We can create a non-polymorphic class that provides this behavior:
public class Subject { private List<Observer> observers = ArrayList<Observer>(); public void register(Observer o) {observers.add(o);} public void notify() { for (Observer o : observers) o.update(); } }
Now any observable can inherit this class and enjoy the implementation of register and notify. Note that there is no polymorphism involved, or intended. We simply want the implementation.
Of course the creators of Java were too wimpy to add true multiple inheritance. So by inheriting Subject, we use up the one and only inheritance slot for our observable class. That's a shame. If Java had true multiple inheritance then we'd be able to mix-in simple implementations like this, with other classes.
As things stand, it is probably best to use composition to bring Subject into the observable, and use delegation to call the register and notify functions. But not because polymorphic interfaces are always better; rather because Java was crippled by the decision to turn multiple inheritance into the bogey man.
 Signature Robert C. Martin (Uncle Bob) | email: unclebob@objectmentor.com Object Mentor Inc. | blog: www.butunclebob.com The Agile Transition Experts | web: www.objectmentor.com 800-338-6716 |
H. S. Lahman - 20 Feb 2008 18:31 GMT Responding to Pitts...
> I was thinking about it, and it seems to me that implementation > inheritance is a convenience that leads to bad design and mistakes. By [quoted text clipped - 4 lines] > an interface in many languages, it just happens to have the associated > implementation. I think it is important to distinguish between simple implementation inheritance and overrides of implementation inheritance. Overrides have proven to be a Really Bad Idea if one cares about maintainability.
However, simple implementation inheritance is almost always benign. To create a situation where maintenance of the generalization can break existing clients requires a large and complicated generalization, which should make one look for delegation solutions in the first place. Also, the foot-shooting examples I've seen with simple implementation inheritance can be attacked on other design grounds.
The problem is that every OOPL I know that supports implementation inheritance also supports overrides because the underlying jump table mechanism is the same. But just because a language supports a feature doesn't mean one needs to use it. I think an OOA/D methodological prohibition of using overrides is sufficient.
> So far, not that big of a deal. The convolution comes when you add in > access modifiers. For instance, in Java you can mark methods/fields as [quoted text clipped - 3 lines] > especially for the "protected" interface, as many programmers don't > design their classes for inheritance but the class gets used that way. IMO, the public/private stuff found in OOPLs is just syntactic sugar that saves the developer some keystrokes at the cost of potentially introducing foot-shooting. There is nothing these qualifiers provide that one could not do with different developer-defined interfaces and associating specific clients with specific interfaces. [In the translation MDA profile I use for OOA, those qualifiers are not available. A full code generator has no problem generating correct code without them.]
[FWIW, I think Protected is a kludge and the only place I would use it is to support a unit test harness -- for which it is very convenient. But let's not go there...]
> To me, polymorphic behavior is important, and "implementation sharing" > is a very useful convenience. I think that they are often co-mingled > concepts that should be separated. I have a few ideas on how I might do > this, at the language level, but I wanted to see if my above thoughts on > the subject stirred any interesting discussions. I agree they are different things. But I think one can prevent them from becoming co-mingled in a detrimental way methodologically. The OOPLs /implement/ OOA/D; methodologies drive OOA/D.
[Note that the OOPLs do much more egregious things, such as combining message and method, simply because they are 3GLs. But those things are rendered harmless by getting the OOA/D right.]
> My main point is that classes should use composition to gain behavior, > possibly with an easy mechanism for delegation. The *only* thing that > should be automatically inherited should be the interface. This > eliminates the need for (and usefulness of) the "protected" access > modifier. Unrestrained composition has its own set of problems as it can trash object cohesion.
 Signature There is nothing wrong with me that could not be cured by a capful of Drano.
H. S. Lahman hsl@pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info@pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|