in reply to Abstract class methods
in thread Interfaces in Perl?

As Randal pointed out, this won't work if the abstract class itself implements stub routines. But it also fails for a bigger reason: At the time you do the can test, the subroutine whose existence you are trying to check has not yet been compiled!

The solution I suggested in the other thread fixes this problem by deferring the check until after compilation is complete. My sample code doesn't deal properly with inherited methods, however. I think a hybrid approach might be effective.

You would use the INIT block approach that I showed, and then in the INIT block, use ->can, and check to see if the returns subroutine was equal to the stub:

sub INIT { ... my $ref = $inheriting_class->can($method); if (! defined($ref)|| defined(&$method) && $ref == \&$method) { $bad = 1; warn ...; } }
What is this doing? It tries to resolve the method with $can, the way Ben shows. If there is no such method, that's bad. If there is a method, it then checks to see if there's a stub routine in the abstract class itself, and, if so, if the subclass's method is actually this stub; if so, that's bad too.

You still have the problem with abstract classes that inherit from other abstract classes, of course, but I think solving these problems should be just a SMOP.

Replies are listed 'Best First'.
Re: Re: Abstract class methods
by gregorovius (Friar) on Dec 01, 2000 at 06:15 UTC
    Mark's (Dominus) solution in the other thread is just what I was looking for. I want all classes where I "use Interface;" to implement and not inherit the methods listed in the Interface package.

    The particular design problem I need this for is one in which I need each class, regardless of what base class they inherit from, to implement themselves a group of methods.

    Java provides a built in facility for doing this, the "implements" keyword, which works almost exactly as the "use Abstract;" Mark proposes. (To it I would suggest adding the word "Interface" to the abstract class name, to achieve Java-like clarity through convention). Thus his example would look like this:

    package DuckInterface; use Carp; my @inheritors; sub import { my $caller = caller; push @inheritors, $caller; } my @abstract_methods = qw(swim fly); sub INIT { my $bad = 0; for my $class (@inheritors) { for my $meth (@abstract_methods) { no strict 'refs'; unless (defined &{"${class}::$meth"}) { $bad=1; warn "Class $class should implement DuckInterface, but does +not define $meth.\n"; } } } croak "Compilation aborted" if $bad; } 1;
    And a class that "implements" DuckInterface:
    package RedDuck; use DuckInterface; sub swim { "I swim like a red duck"; } sub fly { "I fly like a red duck, and DuckInterface guarantees I do!"; }
      There is no need to have an INIT function. Just have the import function skip the abstract class, and then have it do the check. Works exactly like my example did.
Re (tilly) 2 (try it): Abstract class methods
by tilly (Archbishop) on Dec 01, 2000 at 05:48 UTC
    Please try it and attempt to produce a case of breakage.

    In my tests it works perfectly.

    In theory it should work fine as well.

    If A is an abstract class and B inherits from it then the check that B has implemented the methods does not happen when B calls import on A since A ignores the method call with itself as the class. It only happens when A handles the call to import B into whatever C wanted to use it, at which point B has been compiled and loaded.

      tilly says:
      > In my tests it works perfectly.
      Oh, I see! Yes, that's really clever.