Re: Inheritance vs Delegation: pros and cons
by adrianh (Chancellor) on Jul 28, 2003 at 10:22 UTC
|
My advice would be to only use inheritance if your subclass really has an is-a relationship with the parent class.
Can you swap your class in when somebody else is using the parent class? If not, it's not an is-a relationship.
Thinking about classes with Design by Contract in mind often helps. A subclass should:
- Support all of the class invariants of the parent class.
- Support all of the class post-conditions of the parent class.
- Support all of the class preconditions of the parent class.
If this isn't true then inheritance can rapidly become a burden. You end up with a deep class hierarchy where the sub-sub-sub-class does something completely different from original parent class.
Concerns aren't separated in the code, maintenance becomes a nightmare, and you rapidly create a big ball of mud.
I'm not anti-inheritance (hell, I even think multiple inheritance is a good thing :-) but it should only be used for is-a relationships. Anything else is asking for trouble.
| [reply] |
|
|
(hell, I even think multiple inheritance is a good thing :-)
I would agree here, with the caveat that inheritance should be governed by interfaces. For example, one could say that a hovercraft is-a LandVehicle and is-a WaterVehicle. The differences between LandVehicle and WaterVehicle need to be orthogonal, with that any overlap is atomic.
Explanation of my butchering of English:
- If both LandVehicle and WaterVehicle have method XYZ, XYZ does the same type of action for both. Any added features for one can be overloaded, as necessary.
- If WaterVehicle has method ABC that LandVehicle does not, it does something (and only that thing) that LandVehicle cannot do (such as Dive() or DumpBallast() or the like). It shouldn't be something like SlowDown() where LandVehicle has ApplyBrakes().
Multiple inheritance, in my mind, adds several layers of complexity and programmer work that needs to be thoroughly vetted before being approved. That said, there are a few situations where it's warranted.
------ We are the carpenters and bricklayers of the Information Age. Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement. Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.
| [reply] |
|
|
I usually judge when it is becoming evil by the amount of method overridiing that has to be done. If you find yourself overridding nearly every method with functionality that is completely different to the parent then you are in an evil place. If however your method overriding is mainly introducing a bit of polymorphism and extending methods to handle extended structures in your object then I think you are in a good place.
Thats just a personal metric based on lazyness.
I agree multiple inheritance is good and a nice way to package together your clasess.
not too sure bout delegation will need to check that oot sounds like it's just run time method dispatch
| [reply] |
|
|
It sounds like you're talking about "contracts" here not "interfaces" (as I understand the term).
The interface term usually refers to the method signatures of a class, and these days is pretty much synonymous with the Java system of not having multiple inheritance of behaviour - just interface. Which, as a way of avoiding the problems with multiple inheritance, is throwing the baby away with the bathwater as far as I'm concerned.
Just looking at method signatures isn't enough to figure out whether XYZ "does the same action". This is where contracts can be useful. You explicitly state in your pre-conditions and post-conditions what the requirements and result of the method should be.
Many of the problems that occur with multiple inheritance in languages like Java and Perl disappear in languages like Sather and Eiffel where you have contracts (to keep you using inheritance only when appropriate) and support for multiple inheritance (allowing good resolution of naming issues, diamond inheritance, etc.) built into the core language.
| [reply] |
Re: Inheritance vs Delegation: pros and cons
by gmax (Abbot) on Jul 28, 2003 at 10:20 UTC
|
It is a problem of semantics.
I use inheritance whenever the parent class is a generalization of the descending class. The example given in Class::Delegation is not well chosen. A car can't inherit from a wheel. A car contains wheels. In this case you use delegation.
The appropriate example would be a class vehicle, from which you inherit to create car, truck, tractor, and so on. In this case, it makes sense to use inheritance.
My rule of thumb is, if it makes sense to say derived class IS A parent class, you inherit, otherwise you include the other class inside yours.
It makes sense to say a car is a vehicle, but it does not make sense to say a car is a wheel. Somebody could say that "a car is a wheel, a brake, an engine, and so on," and inherit from all the components. But also a washing machine can have wheels, engines and brakes, but it is not a car. Thus, the inheritance paradigm doesn't apply, and forcing it in such a case can only create confusion.
_ _ _ _
(_|| | |(_|><
_|
| [reply] |
Re: Inheritance vs Delegation: pros and cons
by chunlou (Curate) on Jul 28, 2003 at 17:56 UTC
|
The orthodox teaching is, if it's an a-kind-of relationship, use inheritance; if has-a, use aggregation/composition; if an use relationship, use delegation.
But then, there is design vs implementation. Something could be conceptually inheritance; but delegation implementation could be "better," say, for performance or whatever. (Like, it seems to me not all DBAs favor OO SQL.)
Having to call an instance method through an object that's six or seven levels down the hierarchy is no fun.
The advocacy of the use of inheritance was pretty strong back in the 80's and early 90's. Nowadays, inheritance is more discouraged in many people's teaching in favor of, say, interface (as in Java).
Conceptually and practically, if you think about it, inheritance somewhat violates the very concept of encapsulation. It creates rather strong coupling between classes.
Granted, a change in the parent class automatically cascading to all its children could be a very desirable feature. It's useful when your requirements specification is stable enough. If not, if you need to make dramatic changes in the parents, it could break things everywhere else.
Sure, it breaks things too with delegation or interface, but presumeably since the delegator explicitly indicates which methods to import (and implement), at least, it's clearer where things are going to break.
Not necessarily good or bad, for me, as an development process, if I'm not sure what something is supposed to be for the moment, I tend to prefer to err by using something like delegation until it's rather clear that inheritance makes sense implementation-wise and design-wise (sounds like a backward process in fact).
(There are shove-it-down-your-throat kind of marketing fallacy and disinformation that OOPS and inheritance makes your application and code easier to use. Easy of use is the result of well thought out design and implementation, mostly regardless of the paradigm you're following.)
__________________
Update: Perhaps a digression. Somewhat unique to Perl few other languages share, the possibility of mixing procedural and OO style into the same module could be a neat and confusing feature.
It could be neat for instance (no pun intended) when you implement a least square regression module, only matrix in, matrix out. Procedural style is fine. But it would be nice when you "print" the result matrix, you get a summary instead of raw data. In that case, you need to overload the double quotes operator. Hence, you need to "bless" your output matrix into an object.
From the user's perspective, the module could look procedural except for the magical "print" feature. From the coder's perspective, the module is technically a bit of OO.
With such a module, inheritance becomes a boot point in Perl, especially since it could have subroutines in a module that aren't object's methods.
| [reply] |
Re: Inheritance vs Delegation: pros and cons
by simonm (Vicar) on Jul 28, 2003 at 21:10 UTC
|
I certainly wouldn't say inheritance was evil, but it's not always the best solution.
In the context of Net::LDAP, it might be helpful to consider the various reasons people might subclass or encapsulate LDAP connections, and how those might work together.
For example, let's say that people have created several subclasses of Net::LDAP:
- one like yours, which adds some convenient high-level methods;
- one that better caches results to avoid repeated requests to the LDAP host; and
- one that proxies the LDAP requests over SOAP to a mod_perl server behind a firewall, where the LDAP server is located.
Ideally, I'd like to be able to get an object that has the Express interface, the Caching functionality, and the SOAPProxy implementation, but if they're all just simple subclasses of Net::LDAP, it's awkward to assemble such a beast. (Cf. "diamond pattern".) Perhaps you could accomplish it using mixin classes, but still, ick.
If each of those packages is implemented as a "decorator" or "proxy," using delegation, then things suddenly become simpler:
my $ldap_handle = Net::LDAP::Express->new(
debug => 0,
target => Net::LDAP::Caching->new(
duration => 30,
target => Net::LDAP::SOAPProxy->new(
soap_url => '...'
)
)
);
This kind of mix-and-match composition of functionality provides a combinatorial explosion of possibilities, rather than the incremental addition of a subclass. | [reply] [d/l] |
|
|
Ideally, I'd like to be able to get an object that has the Express interface, the Caching functionality, and the SOAPProxy implementation, but if they're all just simple subclasses of Net::LDAP, it's awkward to assemble such a beast. (Cf. "diamond pattern".) Perhaps you could accomplish it using mixin classes, but still, ick.
While I agree that is is "ick" in Perl (and Java, and many other languages) and decorators are an excellent solution, this is more a language issue than an issue with multiple inheritance in general. In Eiffel, for example, it's very easy since you have a lot of explicit control over how multiple inheritance occurs.
| [reply] |
Re: Inheritance vs Delegation: pros and cons
by bsb (Priest) on Jul 28, 2003 at 23:17 UTC
|
Here's a perspective on one type of inheritence from
outside of the OO mind set. It may give insight into
the true nature of inheritence (no delgation though, sorry)
See
Rees Re:OO for the source. It's a great article.
Implementation inheritance/reuse - having written one pile of code, a similar pile (e.g. a superset) can be generated in a controlled manner, i.e. the code doesn't have to be copied and edited. A limited and peculiar kind of abstraction. (E.g. Java class inheritance.)
There's also specification inheritence there.
Brad | [reply] |
Re: Inheritance vs Delegation: pros and cons
by scrottie (Scribe) on Jul 29, 2003 at 05:28 UTC
|
Hi.
Consider typesafety.pm
rather than Class::Contract. Not only does it declare what is
passed and returned, but it enforces argument types and
expected return types to match the declaration (I forgot
to check actual return types - whoops - next version).
All I ever do is shameless self-plug here - I'm really
sorry. Since I try to write useful things, I like to think
that now and again something might actually be useful.
Someone made a comment about trying to suss something
out 5 delegation levels deep. OO code, ideally, shouldn't
try to navigate more than one or two has-a layers away.
Otherwise, code is unduely interdependent. The alternative
is to proxy the requests. Each object should know how
to get requests one step closer to the source, if indeed
it has-a something that is useful that way. A little
more work up front, but refactoring is much, much easier,
and it is all about refactoring. Besides, if you use
typesafety.pm and you change your interface slightly,
it'll warn you what you broke. For a full writeup,
see LawOfDemeter on perldesignpatterns.com.
To actually address the question at hand, yes, delegate,
by any means. Perl makes it easier to write good, clean
OO - you can use run-time tricks to generate accessors
that delegate, or modules like Class::Delegation. When
writing Java, even if your IDE generates the stubs for
you, you still have to look at all of that code. When
faced with hundreds of thousands of lines of code, my
mind swims, even if it is all perfectly neat and orderly.
In fact, if no unique "landmarks" distinguish one part
from another, I get lost. Inheritance, out of control,
takes on the look and feel of a massive, non OO program.
Every method is a local function in the current object.
Objects just become larger and larger. This creates a
sort of GodObject, PDP where no real structure between objects can be
created because there is only one object, or a few objects
hung directly off of it, and knowledge of the arrangement
of those objects is centralized in the God Object. To
really model things using OO, the LawOfDemeter must kick in,
and objects should take on arrangements nimbley, without
the oppression of a God Object. Put another way, you
should be able to return different objects that represent
different parts of the internal state of your application.
From those objects, you should be able to query other
objects. Each object should know its neighbors. It might
take a few calls to go from point A to point C, but
all of the points aren't lumped together in one place.
I hope this helps.
-scott | [reply] |
|
|
Consider typesafety.pm rather than Class::Contract. Not only does it declare what is passed and returned, but it enforces argument types and expected return types to match the declaration (I forgot to check actual return types - whoops - next version). All I ever do is shameless self-plug here - I'm really sorry. Since I try to write useful things, I like to think that now and again something might actually be useful.
Erm.
Class::Contract implements design by contract in perl - which has direct relevance to the inheritence vs delegation issue. Contracts help make the implicit contracts involved in isa relationships explicit in the code itself and encourage good use of inheritance and delegation.
typesafety, while cute, is to do with type safety - a completely separate issue. I don't see how it relates to the inheritence vs delegation issue. You can get in exactly the same mess in statically typed languages like C++ and Java.
| [reply] |