The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Class::Cloneable - A base class for Cloneable objects.

SYNOPSIS

  package MyObject;
  our @ISA = ('Class::Cloneable');

  # calling clone on an instance of MyObject 
  # will give you full deep-cloning functionality

DESCRIPTION

This module provides a flexible base class for building objects with cloning capabilities. This module does it's best to respect the encapsulation of all other objects, including subclasses of itself. This is intended to be a stricter and more OO-ish option than the more general purpose Clone and Clone::PP modules.

METHODS

Public Method

clone

This provided method will deep copy itself and return the clone, while respecting the encapsulation of any objects contained within itself.

For the most part, this will just "do the right thing" and can be used as-is. If however, you need a more specialized approach, see the section below for details on how you can override and customize this methods functionality.

Inner Package

Class::Cloneable::Util is a protected inner package, meaning that it can only be used by Class::Cloneable or it's subclasses. If an attempt is made to use it outside of that context, an exception is thrown.

This inner package is provided as a means of performing fine grained custom cloning operations for users who choose to or need to override the clone method provided by Class::Cloneable. Here is a basic example:

  package MyMoreComplexObject;
  our @ISA = ('Class::Cloneable');
  
  sub clone {
    my ($self) = @_;
    my $clone = {};
    $clone->{dont_clone_this} = $self->{dont_clone_this};
    $clone->{clone_this} = Class::Cloneable::Util::clone($self->{clone_this});
    return bless $clone, ref($self);
  }

NOTE: Many of the functions provided in this package require a $cache argument, which is a HASH reference that is used internally to keep track of the items already cloned to avoid cloning any circular references more than once. The only function here which does not require a $cache argument is the clone function, which will initialize its own $cache if one is not present.

clone ($to_clone, $cache)

For most custom cloning, just calling this function will be enough. The argument $to_clone is required, but the $cache argument can be omitted. This is only allowed in this function, all other functions in this package require that the $cache argument is a HASH reference.

This function will attempt to clone the $to_clone argument following these guidelines:

If $to_clone is a Class::Cloneable derived object and $cache is undefined

Then it is assumed that you are requested a "root" level cloning and the Class::Cloneable object is deconstructed and its internals are cloned. This results in a deep copy of the object in $to_clone, which will recursively call this Class::Cloneable::Util::clone function for each internal element.

If $to_clone is a Class::Cloneable derived object and $cache is not undefined

This is not considered a "root" level cloning so the clone method is called on the $to_clone object. By doing this, we respect the encapsulation of the Class::Cloneable subclass, in case it's clone method has been overridden with custom behavior.

If $to_clone is an object with an available clone method

This will call the clone method found and assume the value returned is a proper clone of the value in $to_clone.

If $to_clone is an object without an available clone method

We assume that because there is no clone method available to the object found stored in $to_clone, that the object does not want to be cloned. The idea is that we should respect the encapsulation of the object and not try to copy it's internals without it's permission.

If $to_clone is a reference

We will deep copy the reference value in $to_clone, which will result in recursive calls to the Class::Cloneable::Util::clone function for all the internal values found inside $to_clone. This will also properly clone any tied references, being sure to tie the clones as well.

It is important to note that any CODE, RegExp and GLOB references are not copied, only passed through. Short of some really insane perl code, this is pretty much not possible (nor is it really recommended).

If $to_clone is not a reference

We will simply return a copy of the value in $to_clone.

As I said, this function (clone) is the central function in this inner package. All other functions are called by this function, and unless you have really specific needs will likely never been used by others.

cloneObject ($to_clone, $cache)

This function will assume that $to_clone is an object, and try to follow the guidelines above where they pertain to objects. However, if $to_clone is not an object, and instead an unblessed reference, this function will pass $to_clone to cloneRef for cloning.

If $to_clone is not a reference of some kind and $cache is not a HASH reference an exception will be thrown.

cloneRef ($to_clone, $cache)

This function will deep copy the reference value in $to_clone, which will result in recursive calls to the Class::Cloneable::Util::clone function for all the values found inside $to_clone.

If $to_clone is not a reference of some kind and $cache is not a HASH reference an exception will be thrown.

CAVEATS

This module places no restrictions upon it's subclasses, the user is free to compose their subclasses with just about any type of referent they like; HASH, SCALAR, ARRAY being the most common. However some of the more exotic and rare object construction styles will likely not work as expected (if at all).

For instance, CODE references and closures are incredibly difficult (if not impossible) to clone correctly, objects created from them are not recommended as they are unlikely to work since we cannot accurately clone them. GLOB and RegExp references are also not cloned, and so will not work correctly. However, there is nothing preventing you from just using Class::Cloneable for the sake of interface polymorphism, and implementing your own clone method with these types of objects.

It is also doubtful that this will work with things like the "Inside-Out" object technique since with this technique object data is not actually stored in the instance, but in a lexical package variable which is keyed by the stringified instance. We can only copy what is found within the actual object instance, and not loosely linked data.

BUGS

None that I am aware of. Of course, if you find a bug, let me know, and I will be sure to fix it.

CAVEATS

This module makes an attempt to handle tied references, however, the way it approaches them is not ideal and potentially wrong in some cases. So use this with care in such situations.

CODE COVERAGE

I use Devel::Cover to test the code coverage of my tests, below is the Devel::Cover report on this module test suite.

 ------------------------ ------ ------ ------ ------ ------ ------ ------
 File                       stmt branch   cond    sub    pod   time  total
 ------------------------ ------ ------ ------ ------ ------ ------ ------
 Class/Cloneable.pm        100.0  100.0  100.0  100.0  100.0  100.0  100.0
 ------------------------ ------ ------ ------ ------ ------ ------ ------
 Total                     100.0  100.0  100.0  100.0  100.0  100.0  100.0
 ------------------------ ------ ------ ------ ------ ------ ------ ------

SEE ALSO

If you need a general purpose cloning module, this is not the module for you, I recommend either Clone (because it's XS and fast) or Clone::PP (if you don't have access to a C compiler). This module only is meant to be used as a base class for objects which need to have cloning abilities, and can not be used outside of that environment.

I want to say first, that this module's code was based heavily on the code found in Clone::PP by Matthew Simon Cavalletto (which in turn was initially derived from Ref.pm by David Muir Sharnoff). Using the code in Clone::PP as a basis for the code in this module saved me a lot of time and I owe a debt to that module and it's author for releasing it Open Source so that this was possible.

Now, you may be wondering why I chose to make yet another cloning module?

My first reason has to do with how these other modules handle objects. Neither Clone and Clone::PP correctly respect object encapsulation. My personal feelings is that if an object does not want to be cloned, it should not be allowed to be cloned (for instance, you would never want to copy a Singleton object, as the whole idea behind a Singleton is that there is only one of them). Upon encountering an object, both Clone and Clone::PP will deep copy it, and therefore violate its encapsulation.

To be fair, if we are talking about really strict encapsulation, then Class::Cloneable violates this too, since it can be used to copy a subclass of Class::Cloneable in a way which technically violates its internal encapsulation. However, this is only for the "root" object, and any subsequent Class::Cloneable objects will be cloned using the clone method (and therefore respecting encapsulation). So while it is not perfect, I feel it is a good compromise between requiring all classes to compose their own clone method and allowing arbitrary deep cloning.

The second reason was to provide more flexibility for overriding a clone method in a subclass. The protected inner package of Class::Cloneable::Util can be used to provide a more fine grained approach to the cloning of your object's internals.

ACKNOWLEDGMENTS

Thanks for Matthew Simon Cavalletto for writing Clone::PP which this module is based upon.

AUTHOR

stevan little, <stevan@iinteractive.com>

COPYRIGHT AND LICENSE

Copyright 2004-2006 by Infinity Interactive, Inc.

http://www.iinteractive.com

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