NAME
Class::SelfMethods - a Module for supporting instance-defined methods
SYNOPSIS
use Class::SelfMethods;
package MyClass;
@ISA = qw(Class::SelfMethods);
use strict;
sub _friendly {
my $self = shift;
return $self->name;
}
package main;
no strict;
my $foo = MyClass->new( name => 'foo' );
my $bar = MyClass->new( name => 'bar', friendly => 'Bar');
my $bas = MyClass->new( name => 'bas',
friendly => sub {
my $self = shift;
return ucfirst($self->_friendly);
}
);
print $foo->friendly, "\n";
print $bar->friendly, "\n";
print $bas->friendly, "\n";
$bas->friendly_SET('a reset friendly');
print $bas->friendly, "\n";
$bas->friendly_SET( sub { my $self = shift; return uc($self->_friendly) });
print $bas->friendly, "\n";
$bas->friendly_CLEAR;
print $bas->friendly, "\n";
DESCRIPTION
Class::SelfMethods
merges some features of other Object Oriented languages to build a system for implementing more flexible objects than is provided by default in Perl.
The core features I was looking for when I wrote Class::SelfMethods
were:
- Class-based inheritance hierarchy
-
I wanted to retain Perl's normal class-based inheritance hierarchy rather than to write (or use) a completely prototype based system. If you are looking for a purely prototype based system, see Sean M. Burke's
Class::Classless
. My reasoning on this is that it is easier in file based languages (as opposed to world based languages like Self) to code class based inheritance hierarchies (which are largely static) than to code object based inheritance hierarchies (since objects in such languages have a dynamicism that is not granted to classes). - Instance-defined method overriding
-
I wanted instances to be able to override their class-defined methods. In the example above, the
$bas
object has its ownfriendly
method. Instance-defined methods are passed the exact same parameter list as class-defined methods. - Subroutine/Attribute equivalence
-
Borrowing from Self, I wanted to be able to treat methods and attributes similarly. For instance, in the above example the
$bar
object has an attributefriendly
, whereas the$bas
object has a methodfriendly
, and the$foo
object uses the class-defined method. The calling syntax is independent of the implementation. Parameters can even be passed in the method call and they will simply be ignored if the method is implemented by a simple attribute
In addition to those core features, I (and Damian) had a wish list of additional features:
- Simple syntax
-
I wanted the system to be reasonable easy to use for both implementers of classes and users of objects. Simple syntax for users is more important than simple syntax for implementers.
- Full support for
SUPER
type concepts -
I wanted instance-defined methods to be able to call the class-defined methods they replace.
- Support for calling methods at instantiation time
-
In some circumstances, rather than deal with multiple inheritance it is easier to have a class-defined object method that sets up the various instance-defined methods for a given object. To support this, the
new
method allows deferred method calls to be passed in as parameters. - Modifying objects post-instantiation
-
I originally had no need for modifying objects post-instantiation, but Damian Conway thought it would be a Good Thing (TM) to support. Being so very good at these sorts of thing, he instantly came up with a good general syntax to support such. Method calls that end in a
_SET
result in the first parameter being assigned to the attribute/method. I noticed one remaining hole and added support for_CLEAR
.
HOW TO
Write A Class
Your class should inherit from Class::SelfMethods
. The class-defined instance methods should be defined with a leading underscore and should be called without a leading underscore. Don't do anything silly like writing methods whose proper names have a leading underscore and whose definitions have two leading underscores - that's just asking for trouble.
Do not, of course, make use of attributes that have leading underscores - that's also just asking for trouble. Also, do not access attributes directly (i.e. $self->{foo}
). That will prevent people who use your class from substituting a method for an attribute. Instead, always read attributes by making the corresponding method call ($self->foo
).
If you need to call SUPER::methodname
, call SUPER::_methodname
.
Create An Instance
The default new
method uses named parameters. Unless you are certifiable, you will too. To specify attributes, simply use the syntax name => 'value'
and to specify a method use name => sub { my $self = shift; . . . }
. Note that methods and attributes are interchangeable.
Modify An Instance
Method calls that end in a _SET
will result in their first parameter being assigned to the appropriate attribute/method. For instance, in the SYNOPSIS
I use $foo->friendly_SET
to specify both a value and a method for friendly
. Method calls that end in a _CLEAR
will delete that attribute/method from the object.
Installation instructions
Standard module installation procedure.
INTERNALS
AUTOLOAD
This is the heart of the system. Every method call passes through AUTOLOAD
because the method is called without the leading underscore, but is defined in the class hierarchy with the leading underscore.
AUTOLOAD
starts by stripping off the module name from $AUTOLOAD
. It checks first for _SET
and _CLEAR
method calls and does the right thing in those situations. If it's a normal method call, it then searches for an entry in $self
's hash with that name. If it finds one and it is not a CODE
reference, it returns the value. If it is a CODE
reference, it calls the subroutine passing it $self
and whatever parameters AUTOLOAD
was originally passed. If it doesn't find an entry in the hash, it prepends an underscore to the method name and uses can
to test whether the object is capable of handling the method call. If it is, AUTOLOAD
calls the method (with the leading underscore) and passes it whatever parameters AUTOLOAD
was originally passed. If not, it returns silently (this is designed to mimic a non-existent hash entry).
new
The new
method supplied in Class::SelfMethods
provides one interesting twist on an otherwise standard named parameters constructor. It strips out any passed parameters that have leading underscores and stores them away. It then creates the hash ref from the remaining parameters and blesses it appropriately. Finally, it takes the stored parameters that have leading underscores and makes the matching method calls - the key is used for the method name and the value is dereferenced to an array and passed as parameters.
AUTHOR
Toby Everett, teverett@alascom.att.com