Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Re: Class::Interface -- isa() Considered Harmful

by adrianh (Chancellor)
on Jan 19, 2003 at 17:33 UTC ( [id://228192]=note: print w/replies, xml ) Need Help??


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

Since nobody else has mentioned them - there are a couple of similar modules already on CPAN.

  • interface: Checks that your module supports the methods that a given set of modules support.
  • Object::Interface: Allows the quick declaration of a purely abstract base class, with compile time errors if the sub-class doesn't support the specified errors.

Interfaces, Interfaces in Perl? and Re: Interfaces seem to cover similar themes too.

I always saw Java interfaces as a response to the naming resolution problems of C++'s implementation of multiple inheritance. Personally, I think they threw out the baby with the bathwater by only allowing multiple-inheritance of interface :-)

Compile time warnings are a good thing obviously, but that's a separate issue from interface vs. abstract base classes.

I have to admit, I still don't have a handle on how what you want is different from Java-ish interfaces, or inheriting from an abstract class. Can you give an example that shows where abstract classes / interfaces wouldn't be enough?

(If it is different from the Java concept I strongly agree with fever that it should be called something else to avoid confusion :-)

You said:

  • I'd like to be able to check that an object I receive from somewhere can handle the operations I'm about to perform on it
  • I'd like that check not to dictate the object's implementation of those operations

Inheriting from an abstract base class seems to meet both of these goals - or am I missing something?

There could be an argument for having something like this as a way to retrofit an "abstract" interface onto a pre-existing class. I could see that being useful when you don't control the source.

But if you do control the source expressing the interface using an abstract class (maybe using Attribute::Abstract, Class::Virtual or Class::Virtually::Abstract) would seem more straightforward.

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

Replies are listed 'Best First'.
Re: Re: Class::Interface -- isa() Considered Harmful
by chromatic (Archbishop) on Jan 19, 2003 at 18:26 UTC

    Inheriting from an abstract base class is dictating an object's implementation. To me, there's a world of difference between saying "even though this mock object inherits absolutely no data or behavior from this class hierarchy, it's a member of this class hierarchy" and "this mock object has the same fingerprint as objects of this class and can be used in their place".

    A mock object is not a DBI object. It's not a CGI object. It can act like either though.

    I think there's a broader principle here -- substitutability. Inheritance is not the only means by which objects of two different classes can be substitutable. There's also composition and delegation (the "has a" relationship) and equivalence of behavior (the "acts like a" relationship). Making an abstract class would work, but it doesn't express the nature of the substitutability.

    I'd like to avoid forcing composition, delegation, and equivalence relationships into the inheritance scheme. If my substitutable object does not fit within the class hierarchy conceptually, why should I have to lie to the compiler and all future maintenance programmers and say that it does?

    (I'm very much in agreement with your comments on Java, though. Pity that a good concept sucked up the proper terminology so much that people think I like that approach.)

      I think what you're looking for in place of "fingerprint" is "(interface) contract" - the concept that a class "promises to behave like (f.ex) a CGI object". And indeed, groping around on CPAN and finding Class::Contract, its goals seem to be much like what you are talking about.

      Makeshifts last the longest.

      To me, there's a world of difference between saying "even though this mock object inherits absolutely no data or behavior from this class hierarchy, it's a member of this class hierarchy" and "this mock object has the same fingerprint as objects of this class and can be used in their place".

      To me there isn't. I think that's the basis for my confusion. To me the inheritance hierarchy is the exact way you express the relationship between different objects with the same "fingerprint" (which am reading as contract).

      I don't understand what advantage inventing a different kind of hierarchy gives you.

      A mock object is not a DBI object. It's not a CGI object.

      Why not? Seriously :-)

      Most of my mocks are implemented as full classes and use the @ISA hierarchy to identify themselves.

      I'd like to avoid forcing composition, delegation, and equivalence relationships into the inheritance scheme. If my substitutable object does not fit within the class hierarchy conceptually, why should I have to lie to the compiler and all future maintenance programmers and say that it does?

      I don't see how composition or delegation would come into the inheritance scheme. They're not ISA inheritance relationships. If somebody says a car-driver isa car or a library isa book they're just plain wrong.

      On the other hand equivalence is, to me, what ISA relationships are all about.

      Can you give an example that shows where ISA wouldn't be appropriate?

        Inheritance does two completely different things in Perl.

        First, it expresses a conceptual relationship. This is the hierarchy you mention. If you need to model buildings, a library is a more specific type of building. So is a restaurant.

        Second, it provides a place to look for methods called on the object that aren't defined in the class itself. This is inheritance of behavior.

        There are plenty of other ways to make sure all of the methods are in place. You can define them all yourself. You can set up an AUTOLOAD scheme, as Test::MockObject does. You can use mixins. You can delegate to another object (with a proxy or a bridge). You can contain another object (with composition).

        I can take care of #2 by any of those other methods. That's what my mocks do. Why should I have to rely on inheritance to provide #1?

        I'd rather have a more general solution, one that allows me to say "an object of this type behaves as an object of this other type". If that's in place, inheritance can still work just fine -- derived objects are automatically marked as being able to act as objects of their parent classes.

        That's exactly what Class::Interface does.

        Can you give an example that shows where ISA wouldn't be appropriate?

        A Car object contains for Wheel objects. I have code that wants to find the circumference of all Wheels in my universe, calling get_circumference() to do so. It looks at all of the objects in my universe to see if they can handle the operations a Wheel can handle. Since Car does not inherit from Wheel, it can't check isa(). If Car is marked as having the same fingerprint as Wheel, it will work.

        I'm not interested in any solution that suggests "You should have an abstract base class for hasWheel or delegatesToWheel." I just want to be able to say "Car can handle messages to Wheel" without expressing a relationship that's not there or exposing how the Car handles Wheel messages.

      Pity that a good concept sucked up the proper terminology...

      On the terminology front I've just come across the wonderful term Duck Typing to describe what (I think) you're talking about (if it walks like a duck, talks like a duck, etc.).

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://228192]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2024-04-19 20:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found