package Class::Flyweight; =head1 NAME Class::Flyweight - implement the flyweight pattern in OO perl =head1 SYNOPSIS package MyPackage; use strict; use Class::Flyweight; #Create the flyweight object store my $flyweight = Class::Flyweight->new; #make a new flyweight object sub new { my $class = shift; my $self = $flyweight->clone({}, $class); return $self; } #fetch the data structure from the object #store, then access it directly. sub get_name { my $self = shift; my $data = $flyweight->fetch($self); return $data->{name}; } #this is required to ensure proper clean-up sub DESTROY { my $self = shift; $flyweight->delete($self); } 1; =head1 ABSTRACT This module provides a lightweight framework for developing perl modules that have private internal data structures. It should be impossible to modify an object's data stucture in ways not intended you, the author, thus providing true encapsulation of object data. =head1 DESCRIPTION =head2 What is the flyweight pattern? The flyweight pattern is a little known technique for implementing objects. The main difference between the flyweight pattern and a normal object, is that the blessed object is very small, and does not carry around the object information. Rather it acts like an "index" and knows how to get at the object data, but can only do so from within the lexical scope that we define the Class::Flyweight object in. The key is that the object data storage is done inside the scope that the class and it's methods are defined in. Only the class author can access the data, and provide interfaces for others to do the same. It *should* be impossible to get at this data any other way, thus providing true object encapsulation. The concept was first introduced to me in Damian Conway's amazing book, Object Oriented Perl2. Class::Flyweight is based on his examples on page 302, and encapsulating the functionality he illustrates, hiding the details of accessing the object store from you. =head2 How do I use it? To begin using the flyweight pattern, you must create a new flyweight object, using the c constructor. Make sure that this is done inside the perl module you are writing. Next, make sure that all your methods pull the object data from the data store using the C method. It will return a valid reference to the data inside the object. Please make sure that you do not allow direct access to modify the object data from your methods. Otherwise, that defeats the purpose of Class::Flyweight, and it would be better and simpler at that point to use pure arrays/hashes/etc. One final thing you need to remember is to make a DESTROY method for your class. It will be responsible for calling the C method to ensure clean-up of the object data when the object goes out of scope. Normally perl's garbage collection will clean out an object once the number of variables referencing it has dropped to 0. In this system, the object and the object's data are not tied directly. Perl's garbage collection facility has no way of knowing it needs to clean the data out unless you tell it to explicitly though using the C method. The Synopsis contains a package that shows a complete working example. =cut use strict; use Carp; use vars qw($VERSION); $VERSION = '0.01'; #Internal Method. Ensure that any objects created #with this class are cleaned up properly. sub DESTROY { my $self = shift; #by the time DESTROY is called, we must #not have any objects left. croak 'usage: must do $flyweight->delete($self) in $self->DESTROY' if %$self; } =head1 METHODS =over 4 =item * C my $flyweight = Class::Flyweight->new; The C contructor is responsible for creating a new flyweight object. =cut sub new { my $class = shift; return bless {}, $class; } =item * C my $new_object = $flyweight->clone($reference); my $new_object = $flyweight->clone($reference, $class); The C method will create a new object blessed into $class, that allows you to access the data in the $flyweight data store. The second argument is a reference to a perl data type. Any perl reference can be used. =cut sub clone { @_ >= 2 or croak 'usage: $flyweight->clone(OBJECT,[CLASS])'; my $self = shift; my $index; $index = rand until $index and not exists $self->{$index}; $self->{$index} = shift; return bless \$index, shift || scalar caller; } =item * C my $data_ref = $flyweight->fetch($self); The C method returns a reference to the object data inside the object store. The reference can be any valid reference, and is not limited to hashes or arrays. Any object reference can be retrieved from the object store. =cut sub fetch { @_ == 2 or croak 'usage: $flyweight->fetch(OBJECT)'; my $self = shift; my $index = shift; return $self->{$$index}; } =item * C $flyweight->delete($self); The C method is responsible for cleaning out the $self object from the data store, once it has gone out of scope. It must be called, before destruction, either by the class author, or ideally in the classes DESTROY method. =cut sub delete { @_ == 2 or croak 'usage: $flyweight->delete(OBJECT)'; my $self = shift; my $index = shift; #decrement the reference count, so the object #data is cleaned up properly delete $self->{$$index}; } =item * C while(my $object = $flyweight->each) { #... do something with/to each object ... } The C Class method will iterate and return each object in the flyweight object store. =cut sub each { wantarray and croak 'usage: each() only returns 1 argument'; my $self = shift; my $next_key = each %$self; return bless \$next_key, scalar caller if defined $next_key; } =pod =back =head1 BUGS None that I know of. Please let me know if you find anything, or have any suggestions. =head1 AUTHOR Dan Kubb . Vancouver, BC, Canada. Intitial revision July 7, 2001. =head1 ACKNOWLEDGEMENTS =over 4 =item * A special thanks goes out to Damian Conway for his excellent book, Object Oriented Perl, which first introduced me to the flyweight concept, among many others. =item * The folks at perlmonks.org for providing and contributing to a forum where I have learned more about perl than at any other time since I first typed #!/usr/bin/perl. =back =head1 COPYRIGHT Class::Flyweight is free software; you can redistribute it and/or modify it under the terms of the "Perl Artistic License". =head1 SEE ALSO =head2 Object Encapsulation Made Easy See the following essay by Damian Conway, that explains how flyweight objects work, and provides several other alternatives to providing object encapsulation. http://www.csse.monash.edu.au/~damian/TPC/1999/Encapsulation/Paper.html =cut 1;