http://qs1969.pair.com?node_id=44300
Category: Miscellaneous
Author/Contact Info
Description: This grew out of Interfaces in Perl?. This module provides a mechanism for creating what are called abstract base classes - classes that do not themselves define certain methods but impose an automatic check that they have been properly defined in derived classes.

Here is an example where Foo and Bar are two abstract base classes and Baz inherits from them. This was (not surprisingly) my test example. First Foo.pm, which requires a method called foo:

package Foo; use AbstractClass; @ABSTRACT_METHODS = qw(foo); sub foo {print "Subclasses are not allowed to use me\n";} 1;
Now Bar which is another abstract class inheriting from Foo, implementing foo, and requiring bar. I have shown the other way to declare an abstract method:
package Bar; use AbstractClass qw(bar); use Foo; @ISA = qw(Foo); sub foo {print "I am an allowable foo\n";} 1;
And finally, Baz.pm, a derived class inheriting from Bar:
use Bar; @ISA = qw(Bar); #sub foo {print "This is method foo\n";} sub bar {print "This is method bar\n";} 1;

UPDATE
Taking into account comments from Dominus (in a post which I think did not really deserve deletion) I have changed the name to AbstractClass, edited the documentation, and allowed abstract classes that do not define any new methods of their own. (This to allow classes that merely override a few methods.) I did not attempt to override bless, and won't pending some discussion. On the whole I don't much like overriding as a concept, and I am not sure that the gain is worth the pain.

Oh, and I added a version number. :-)

UPDATE 2
After some thought and an email to Damian I decided to implement a restriction to make it harder to create objects in an abstract class. While it would not be hard to make the check far stronger, doing so involves overriding bless everywhere, which is something I don't like philosophically. Also it would slow down the creation of objects from every constructor slightly. (As opposed to just slowing down constructors in abstract classes.)

UPDATE 3
Improved the check that subclasses overrode methods. Now you can usefully have an abstract class that inherits from a normal class and lists methods in that class which will need to be overridden (presumably because you have changed the implementation in some basic way). I am not sure that anyone would want to do so, but this issue was bugging me.

package AbstractClass;
$VERSION = 0.12;
use Carp;

sub bless ($@) {
  my $class = $_[1] || caller();
  if (exists $Registered{$class}) {
    confess("Cannot bless objects into abstract class $class");
  }
  else {
    CORE::bless(shift, $class);
  }
}

$text = '
#line 1 "(AbstractClass loaded for MYPACK)"
  package MYPACK;

  sub import {
    my $pkg = shift;
    return if $pkg eq "MYPACK";
    foreach my $meth (@ABSTRACT_METHODS) {
      my $does = $pkg->can($meth);
      if (not defined($does)) {
        Carp::croak("Class $pkg must define $meth for class MYPACK");
      }
      else {
        my $override = MYPACK->can($meth);
        if (defined ($override) and $does == $override) {
          Carp::confess("Class $pkg cannot inherit $meth from MYPACK")
+;
        }
      }
    }
    $pkg->SUPER::import(@_);
  }
  1;
';

sub import {
  shift; # Don't need my class name
  my $base = caller();
  $Registered{$base}++;
  *{"$base\::bless"} = *bless;
  @{"$base\::ABSTRACT_METHODS"} = @_;
  my $eval_str = $text;
  $eval_str =~ s/MYPACK/$base/g;
  eval($eval_str) or confess("Cannot execute:\n$eval_str\n\nError $@")
+;
}

1;

__END__

=head1 NAME

AbstractClass - Makes classes into abstract classes.

=head1 SYNOPSIS

In module SomeAbstractClass.pm:

  package SomeAbstractClass;
  use AbstractClass;
  @ABSTRACT_METHODS = qw(foo bar);

or more compactly:

  package SomeAbstractClass;
  use AbstractClass qw(foo bar);

=head1 DESCRIPTION

An abstract class is a class which defines methods
that must be overridden in classes that wish to inherit
from it which are not themselves abstract classes.
Normally the abstract class will then provide methods
whose implementations need these methods to exist.  
Whether or not it is an abstract class, it would crash
at some point if the methods were missing.  By moving
the check to when you first load the class, you can find
problems immediately and get better reporting of the
requirement.

It is customary to not have any objects in an abstract
class.  This module provides some enforcement of this
rule by overriding bless in abstract classes.

=head1 BUGS

Abstract classes are implemented with special import
functions.  Therefore this does not play well with
Exporter.  Also due to unfortunate restrictions in the
implementation of Perl's SUPER pseudo-package, multiple
inheritance will not play well with abstract classes.  If
you wish to use the capabilities of abstract classes and
multiple inheritance, you may wish to look at the more
intrusive Class::Contract.

This mechanism also means that if you define multiple
classes in a single file then you must manually import
each derived class to test it.  When each class is in its
own module, this happens automatically when you use the
module.

It is not hard to get around the rule that objects cannot
belong to an abstract class.  To fix that would require
overriding the core bless method, which brings up issues
of its own.

=head1 AUTHOR

AbstractBase was written by Ben Tilly <ben_tilly@hotmail.com>.
This module may be copied and modified on the same terms as
Perl itself.