NAME

Object::LocalVars - Outside-in objects with local aliasing of $self and object variables

SYNOPSIS

      package My::Object;
      use strict;
      use Object::LocalVars;

give_methods our $self; # this exact line is required

our $field1 : Prop;

our $field2 : Prop;

sub as_string : Method {

        return "$self has properties '$field1' and '$field2'";
      }

DESCRIPTION

Do not use for production purposes!

This is a experimental module I developed when exploring inside-out objects. It is no longer supported, but is left on CPAN as an example of the kind of strange OO approaches that are possible with Perl.

This module helps developers create "outside-in" objects. Properties (and $self) are declared as package globals. Method calls are wrapped such that these globals take on a local value that is correct for the specific calling object and the duration of the method call. I.e. $self is locally aliased to the calling object and properties are locally aliased to the values of the properties for that object. The package globals themselves only declare properties in the package and hold no data themselves. Data are stored in a separate namespace for each property, keyed off the reference memory addresses of the objects.

Outside-in objects are similar to "inside-out" objects, which store data in a single lexical hash (or closure) for each property, which is keyed off the reference memory addresses of the objects. Both differ from classic Perl objects, which hold data for the object directly using a blessed reference or closure to a data structure, typically a hash. For both outside-in and inside-out objects, data are stored centrally and the blessed reference is simply a key to look up the right data in the central data store.

The use of package variables for outside-in objects allows for the use of dynamic symbol table manipulation and aliasing. As a result, Object::LocalVars delivers a variety of features -- though with some corresponding drawbacks.

Features

Drawbacks

Design principles
Simplicity

Object::LocalVars was written to simplify writing classes in Perl by removing the need for redundant and awkward code. E.g.:

     sub foo {
         my $self = shift;                 # e.g. repetitive
         push @{$self->some_list}, "foo";  # e.g. awkward
     }

Instead, Object::LocalVars uses a more elegant, readable and minimalist

approach

our $some_list : Prop;

sub foo : Method {

         push @$some_list, "foo";
     }

As with Perl, "easy things should be easy; difficult things should be possible" and there should be a smooth learning curve from one to the other.

Accessors and mutators

A major objective of Object::LocalVars is a significant reduction in the need for accessors (and mutators). In general, accessors break the OO encapsulation paradigm by revealing or allowing changes to internal object state. However, accessors are common in Perl for two big reasons:

         $self->{created}; # correct
         $self->{craeted}; # typo
         $self->craeted(); # typo, but caught at compile time

As a result, the proliferation of accessors opens up the class internals unless additional protections are added to the accessors to make them private.

With Object::LocalVars's aliasing, properties stay private by default and don't need an accessor for typo safety. If protected or public accessors are needed for subclasses or external code to check state, these can be requested as needed.

Terminology
Object-oriented programming suffers from a plethora of terms used to describe certain features and characteristics of classes and objects. Perl further complicates this by using these or related terms for other features entirely (e.g. attributes). (And Perl 6 swaps around these definitions again.) Within this documentation, terms are used as

follows

USAGE
Getting Started
The most minimal usage of Object::LocalVars consists of importing it with "use" and calling the "give_methods" routine:

     use Object::Localvars;
     give_methods our $self;  # Required

This automatically imports attribute handlers to mark properties and methods and imports several necessary, supporting subroutines that provide basic class functionality such as object construction and destruction. To support environments such as "mod_perl", which have no "CHECK" or "INIT" phases, all attributes take effect during the "BEGIN" phase when the module is compiled and executed. The "give_methods" subroutine provides the run-time setup aspect of this and must always appear as shown.

Declaring Object Properties
Properties are declared by specifying a package variable using the keyword "our" and an appropriate attribute. There are several attributes (and aliases for attributes) available which result in different degrees of privacy and different resulting rules for creating accessors.

While properties are declared as an "our" variable, they are stored elsewhere in a private package namespace. When methods are called, a wrapper function temporarily aliases these package variables using "local" to their proper class or object property values. This allows for seamless access to properties, as if they were normal variables. For example, dereferencing a list property:

our $favorites_list : Prop;

sub add_favorite : Method {

       my $new_item = shift;
       push @$favorites_list, $new_item;
     }

Object::LocalVars provides the following attributes for object

properties
          our $prop1 : Prop;
          our $prop2 : Priv;

        Either of these attributes declare a private property. Private
        properties are aliased within methods, but no accessors are created.
        This is the recommended default unless specific alternate
        functionality is needed. Of course, developers are free to write
        methods that act as accessors, and provide additional behavior such
        as argument validation.

our $prop3 : Prot;

        This attribute declares a protected property. Protected properties
        are aliased within methods, and an accessor and mutator are created.
        However, the accessor and mutator may only be called by the
        declaring package or a subclass of it.

our $prop4 : Pub;

        This attribute declares a public property. Public properties are
        aliased within methods, and an accessor and mutator are created that
        may be called from anywhere.

our $prop5 : ReadOnly;

        This attribute declares a readonly property. Readonly properties are
        aliased within methods, and an accessor and mutator are created. The
        accessor is public, but the mutator is protected.

Declaring Class Properties
Class properties work like object properties, but the value of a class property is the same in all object or class methods.

Object::LocalVars provides the following attributes for class

properties
          our $class1 : Class;
          our $class2 : ClassPriv;

        Either of these attributes declare a private class property. Private
        class properties are aliased within methods, but no accessors are
        created. This is the recommended default unless specific alternate
        functionality is needed.

our $class3 : ClassProt;

        This attribute declares a protected class property. Protected class
        properties are aliased within methods, and an accessor and mutator
        are created. However, the accessor and mutator may only be called by
        the declaring package or a subclass of it.

our $class4 : ClassPub;

        This attribute declares a public class property. Public class
        properties are aliased within methods, and an accessor and mutator
        are created that may be called from anywhere.

our $class5 : ClassReadOnly;

        This attribute declares a readonly class property. Readonly class
        properties are aliased within methods, and an accessor and mutator
        are created. The accessor is public, but the mutator is protected.

Declaring Methods

      sub foo : Method {
        my ($arg1, $arg2) = @_;  # no need to shift $self
        # $self and all properties automatically aliased
      }

As with properties, methods are indicated by the addition of an attribute to a subroutine declaration. When these marked subroutines are called, a wrapper function ensures that $self and all properties are aliased appropriately and passes only the remaining arguments to the marked subroutine. Class properties are always aliased to the current values of the class properties. If the method is called on an object, all object properties are aliased to the state of that object. These aliases are true aliases, not copies. Changes to the alias change the underlying properties.

Object::LocalVars provides the following attributes for subroutines:

         sub fcn1 : Method { }
         sub fcn2 : Pub { }

        Either of these attributes declare a public method. Public methods
        may be called from anywhere. This is the recommended default unless
        specific alternate functionality is needed.

sub fcn3 : Prot { }

        This attribute declares a protected method. Protected methods may be
        called only from the declaring package or a subclass of it.

sub fcn4 : Priv { }

        This attribute declares a private method. Private methods may only
        be called only from the declaring package. See "Hints and Tips" for
        good style for calling private methods.

Accessors and Mutators

# property declarations

our $name : Pub; # :Pub creates an accessor and mutator

our $age : Pub;

# elsewhere in code

$obj->set_name( 'Fred' )->set_age( 23 );

print $obj->name;

Properties that are public or protected automatically have appropriate accessors and mutators generated. By default, these use an Eiffel-style syntax, e.g.: "$obj->x()" and "$obj->set_x()". Mutatators return the calling object, allowing method chaining.

The prefixes for accessors and mutators may be altered using the "accessor_style()" class method.

Constructors and Destructors
Object::LocalVars automatically provides the standard constructor, "new", an initializer, "BUILDALL", and the standard destructor, "DESTROY". Each calls a series of functions to manage initialization and destruction within the inheritance model.

When "new" is called, a new blessed object is created. By default, this object is an anonymous scalar. (See "CONFIGURATION OPTIONS" for how to use another type of object as a base instead.)

After the object is created, "BUILDALL" is used to recursively initialize superclasses using their "BUILDALL" methods. A user-defined "PREBUILD" routine can modify the arguments passed to superclasses. The object is then initialized using a user-defined "BUILD". (This approach resembles the Perl6 object initialization model.)

A detailed program flow follows:

During object destruction, the process works in reverse. In "DESTROY", user-defined cleanup for the object's class is handled with "DEMOLISH" (if it exists). Then, memory for object properties is freed. Finally, "DESTROY" is called for each superclass in @ISA which can do "DESTROY".

Both "BUILDALL" and "DESTROY" handle "diamond" inheritance patterns appropriately. Initialization and destruction will only be done once for each superclass for any given object.

Hints and Tips
Calling private methods

Good style for private method calling in traditional Perl object-oriented programming is to call private methods directly, "foo($self,@args)", rather than with method lookup, "$self->foo(@args)". This avoids unintentionally calling a subclass method of the same name if a subclass happens to provide one.

Avoiding hidden internal data

For a package using Object::LocalVars, e.g. "My::Package", object properties are stored in "My::Package::DATA", class properties are stored in "My::Package::CLASSDATA", methods are stored in "My::Package::METHODS", and objects are tracked for cloning in "My::Package::TRACKER". Do not access these areas directly or overwrite them with other global data or unexpected results are guaranteed to occur.

(In a future release of this module, this storage approach should be replaced by fully-encapsulated anonymous symbol tables.)

METHODS TO BE WRITTEN BY A DEVELOPER
"PREBUILD()"

     sub PREBUILD {
         my ($superclass, @args) = @_;
         # filter @args in some way
         return @args;
     }

This subroutine may be written to filter arguments given to "BUILDALL" before passing them to a superclass "BUILDALL". This must not be tagged with a ":Method" attribute or equivalent as it is called before the object is fully initialized. The primary purpose of this subroutine is to strip out any arguments that would cause the superclass initializer to die and/or to add any default arguments that should always be passed to the superclass.

"BUILD()"

     # Assuming our $counter : Class;
     sub BUILD : Method {
         my %init = ( %defaults, @_ );
         $prop1 = $init{prop1};
         $counter++;
     }

This method may be written to initialize the object after it is created. If available, it is called at the end of "BUILDALL". The @_ array contains the original array passed to "BUILDALL".

"DEMOLISH()"

      # Assume our $counter : Class;
     sub DEMOLISH : Method {
         $counter--;
     }

This method may be defined to provide some cleanup actions when the object goes out of scope and is destroyed. If available, it is called at the start of the destructor (i.e "DESTROY").

METHODS AUTOMATICALLY EXPORTED

These methods will be automatically exported for use. This export can be prevented by passing the method name preceded by a "!" in a list after the call to "use Object::LocalVars". E.g.:

use Object::LocalVars qw( !new );

This is generally not needed and is strongly discouraged, but is available should developers need some very customized behavior in "new" or "DESTROY" that can't be achieved with "BUILD" and "DEMOLISH".

"give_methods()"

give_methods our $self;

Installs wrappers around all subroutines tagged as methods. This function (and the declaration of "our $self") must be used in all classes built with Object::LocalVars. It should only be called once for any class.

"new()"

my $obj = Some::Class->new( @arguments );

The constructor. Classes built with Object::LocalVars have this available by default and do not need their own constructor.

"caller()"

my $caller = caller(0);

This subroutine is exported automatically and emulates the built-in "caller" with the exception that if the caller is Object::LocalVars (i.e. from a wrapper function), it will continue to look upward in the calling stack until the first non-Object::LocalVars package is found.

"BUILDALL()"
The initializer. It is initially called by "new" and then recursively calls "BUILDALL" for all superclasses. Arguments for superclass initialization are filtered through "PREBUILD". It should not be called by users.

"CLONE()"
When threads are used, this subroutine is called by perl when a new thread is created to ensure objects are properly cloned to the new thread. Users shouldn't call this function directly and it must not be overridden.

"DESTROY()"
A destructor. This is not used within Object::LocalVars directly but is exported automatically when Object::LocalVars is imported. "DESTROY" calls "DEMOLISH" (if it exists), frees object property memory, and then calls "DESTROY" for every superclass in @ISA. It should not be called by users.

CONFIGURATION OPTIONS
"accessor_style()"

     package My::Class;
     use Object::LocalVars;
     BEGIN {
         Object::LocalVars->accessor_style( {
             get => 'get_',
             set => 'set_'
         });
     }

This class method changes the prefixes for accessors and mutators. When called from within a "BEGIN" block before properties are declared, it will change the style of all properties subsequently declared. It takes as an argument a hash reference with either or both of the keys 'get' and 'set' with the values indicating the accessor/mutator prefix to be used.

If the prefix is the same for both, an combined accessor/mutator will be created that sets the value of the property if an argument is passed and always returns the value of the property. E.g.:

     package My::Class;
     use Object::LocalVars;
     BEGIN {
         Object::LocalVars->accessor_style( {
             get => q{},
             set => q{}
         });
     }

our $age : Pub;

# elsewhere

$obj->age( $obj->age() + 1 ); # increment age by 1

Combined accessor/mutators are treated as mutators for the interpretation of privacy settings.

"base_object()"

     package My::Class; 
     use Object::LocalVars; 
     Object::LocalVars->base_object( 'Another::Class' ); 
     give_methods our $self;

This class method changes the basic blessed object type for the calling package from being an anonymous scalar to a fully-fledged object of the given type. This allows classes build with Object::LocalVars to subclass any type of class, regardless of its underlying implementation (e.g. a hash) -- though only a single class can be subclassed in such a manner. "PREBUILD" (if it exists) is called on the arguments to "new" before generating the base object using its constructor. The object is then re-blessed into the proper class. Other initializers are run as normal based on @ISA, but the base class is not intialized again.

If the given base class does not already exist in @ISA, it is imported with "require" and pushed onto the @ISA stack, similar to the pragma base.

BENCHMARKING

Forthcoming. In short, Object::LocalVars can be faster than traditional approaches if the ratio of property access within methods is high relative to number of method calls. It is slower than traditional approaches if there are many method calls that individually do little property access. In general, Object::LocalVars trades off coding elegance and clarity for speed of execution.

SEE ALSO

These other modules provide similiar functionality and/or inspired this one. Quotes are from their respective documentations.

INSTALLATION

The following commands will build, test, and install this module:

     perl Build.PL
     perl Build
     perl Build test
     perl Build install

BUGS

Please report bugs using the CPAN Request Tracker at <http://rt.cpan.org/NoAuth/Bugs.html?Dist=/home/david/projects/Object-Lo calVars>

AUTHOR

David A Golden (DAGOLDEN)

dagolden@cpan.org

http://dagolden.com/

COPYRIGHT

Copyright (c) 2005 by David A Golden

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the LICENSE file included with this module.