in reply to Class::Interface -- isa() Considered Harmful

I think your basic point could be rephrased as this: "Object Oriented Design != inheritance and class heirarchies". This is a good point and well worth repeating. I've seen that a lot (especially newer) programmers want to solve every OO design question with inheritance, even when composition & delegation would be more natural.

I think one thing that might help would be first class delegation support in a language. I think there's a tendency to feel like inheritance is always okay because its been blessed into the language itself, whereas delegation is just a "hack".

On a sort of side note, I just read Chris Date & Hugh Darwen's "Third Manifesto" book (2nd edition), which is their response to two previous manifestos aiming to integrate relational theory and object oriented programming/design.

In the book, they outline a very detailed and well-defined model for inheritance. In their inheritance model, there are no such things as methods. Methods don't belong to a data type (class), they are just functions, with type dispatch based on the type of _all_ the arguments. The first argument is not special, unlike most OO implementations.

I really like their proposal, except for one thing. It's very data-oriented. If you're using inheritance because you're saying that two things represent a similar kind of data (they share set of properties), it works really well. They use geometrical figures as examples, so a circle inherits from an ellipse, for example. A circle and ellipse both represent similar data (geometric figures), but they don't necessarily have a lot of behavior. When you deal with them you're more interested in data (length of axes, placement on a plane, etc) than asking them to do things for you. And moreover, you want substitutability, so that anywhere you expect an ellipse, you can provide a circle.

OTOH, there are times when you want OO design in order to provide a common interface to multiple, often quite different, things. For example, if you were creating an RPG game, you might have lots of different objects that need to share some common behavior like "hit a creature". Each object may have fairly different sets of data describing the object, but you want to be able to wack things with them, so you give them a common interface.

All this is to set up my conjecture which is that maybe we should talk about two kinds of inheritance. For lack of a better word, one would be "behavior" inheritance (all objects must be able to "hit the thing") and the other "property" (data) inheritance (all objects must be able to tell me their weight in kilograms). With property inheritance, you don't care about behavior, but you want to be able to ask for certain information from all objects (weight, length, color, etc.).

Sometimes you need both, as in the "RPG object" example. But sometimes you might need only one or the other. I wonder if a language which separated the two might be useful.

In case it isn't clear, this relates to chromatic's piece because I think what I'm calling "behavior" inheritance is the same as chromatic's interface suggestion. My "data" inheritance would be more like object properties. In Perl, properties are not really distinguished from methods (behavior), but they could be!

BTW, if I'm describing some theory/language, pointers to it are welcome ;)

  • Comment on Re: Class::Interface -- isa() Considered Harmful

Replies are listed 'Best First'.
Re: Re: Class::Interface -- isa() Considered Harmful
by djantzen (Priest) on Jan 16, 2003 at 06:30 UTC

    All this is to set up my conjecture which is that maybe we should talk about two kinds of inheritance. For lack of a better word, one would be "behavior" inheritance (all objects must be able to "hit the thing") and the other "property" (data) inheritance (all objects must be able to tell me their weight in kilograms).

    I'm close to agreeing with you, and I think the idea can be clarified to answer chromatic precisely. The two types of inheritance are inheritance of interface and inheritance of implementation. The former indicates that the object at hand will respond to the same method calls as its superclass, whereas the latter means that the object will accomplish those tasks using the same code and in the same manner as its parent class. I disagree with the idea that if a subclass merely supports an interface but does not inherit (at least some parts of) its implementation that it lacks an is-a relation with its superclass. A class that implements an interface inherits the public feature set of its parent class and its parent class's type.

    For comparison, look at the concept of the "abstract base class" stemming from C++ and its later evolution to Java interfaces discussed in this recent interview with Scott Meyers, one of the C++ gurus. As a way of avoiding nasty conundrums resulting from C++'s allowance for multiple inheritance, Meyers has argued in his books that a subclass should inherit its implementation from only one of its superclasses. Additional superclasses should be abstract base classes (ABCs) with no data members and only pure virtual functions. In other words, an ABC can at best be instantiated as an empty shell, and its methods declared but not given any bodies and their implementations looked up at runtime in declared subclasses. Thus, all that such classes provide are additional is-a relationships. Java provides shorthand for this with the interface keyword, which guarantees what C++ coders had to see to for themselves. For this reason, placing an interface in a class's @ISA makes perfect sense; it's just rather close to the C++ way.

      Yep, I thought of Java interfaces versus the single "implementation" parent.

      I was thinking of going further. First of all, support Date & Darwen's inheritance model (which is all about inheriting attributes) which includes MI. Define implementation inheritance to not cause the inheritance of implementation. This is what Java interfaces, and chromatic's proposal suggest. Finally, forbid inheritance of interfaces from implementation parents! This last bit is what I think may be a new idea (or at least not present in C++, Java, and certainly not Perl).

      I do think calling it implementation inheritance is a bit confusing, since you're really inheriting attributes (or properties). The actual internal implementation may or may not change in the subclass.

      So if you had a class Car that inherited implementation from WheeledLandVehicle (which may in turn have inherited from some other class), it'd have to provide access to certain attributes such as weight, manufacturer, number_of_wheels, and so forth.

      Interface-wise, Car might inherit interfaces such as Steerable, CanChangeVelocity, Breakable, and so on.

      I should also point out that in the model I'm thinking of, nothing would preclude inheriting both interface and attributes from the same class, which is obviously going to be a common need. But at the same time, when you don't need, or want, to do that, you wouldn't have to.

        I do like the idea of an explicit mechanism in Perl to declare that a class provides its own implementation for an interface, since as it stands now you must search @ISA for the abstract base class and then at runtime see if those methods actually permit themselves to be used. (Note that it's common to die or throw an Exception or Error inside those "abstract" methods in the case they are called directly or not overridden in subclasses.) I think this is sound theoretically -- I reiterate that inheritance of interface constitutes an is-a relationship -- however it is a pain. What I'd like is to say:

        use base Car; use interface Drivable; # a little cleaner than chromatic's example co +de IMHO

        and have the interface pragma enforce the proscription against non-static data members and method implementations. Discovering if a class implements an interface then is the simpler task of querying the implements hash or array.

        The major obstacle to acceptance of the Date & Darwen idea of inheritance is that it's not what most programmers mean when they talk about object-orientation. The definition of an "object" is the synthesis in a single entity of state and behavior. I have no objection to the judicious separation of these two things into i.e., structs or class methods, however a class with methods but no instance data does not define an object, and neither does a class with data but no methods. (Hmm, "Methods without data are empty, data without methods are blind" :^) This is why I'm saying that "implementation" covers all aspects of how a class does its business, including not just the data members, but the member functions as well. Now if all you want out of an object is its data, then subclassing is inappropriate; rather, you should simply pull what you need from the object and store it elsewhere, where you don't need to ensure that it won't be acted upon using its natural inherited behavior.

        I think what you mean is to separate implementation of interfaces from the implementation of attributes they manipulate. Yes, that's a good idea. However, it's too blurry a line in my opinion, so is not something the language can enforce. Rather, it's the framework designer's homework. The model-view-controller paradigm fever mentioned seems indeed to be what you're after. In fact, I consider the MVC approach almost the sole raison d' être for OO as a paradigm equal to functional or logical programming.

        Makeshifts last the longest.

Re: Re: Class::Interface -- isa() Considered Harmful
by bsb (Priest) on Feb 12, 2003 at 00:41 UTC
    This brief article covers some of your points (see 7,8,9) such as (multi)method dispatch, two types of inheritence and inheritence as polymorphism.

    Rees Re:00

    After reading it you may think the OO programming is a little strange.
    Fortunately, perl lets you mix and match your methodologies as appropriate.