scratchpad
radiantmatrix
<div class='THEME'><hr />
<p>In a comment below is a nice, calming Blue theme.
<!--
<code>
/* Calming PerlMonks theme (c)2005 Radiantmatrix */
body {
background: #cad3ea;
color: #000;
margin: 0;
padding: 0;
}
a {
text-decoration: underline;
}
a:link {
color: #00d;
background: transparent;
}
a:visited {
color: #008;
background: transparent;
}
.titlebar {
background: #4b6bc5;
color: #fff;
}
#mb2001titlebar{
padding-right: 6px;
}
.titlechooser {
background: transparent;/* #6c799e; */
color: #00d;
font-size: 110%;
}
.attribution {
font-size: 9pt !important;
font-family: sans-serif;
}
/*** Main Content Area ***/
.main_content {
border-left: 1px solid #4b6bc5;
border-right: 1px solid #4b6bc5;
padding-left: 6px;
padding-right: 6px;
background: #eee;
}
.section_title {
background: #4b6bc5 !important;
color: #fff;
}
.codetext,.code,.inlinecode{
font-family: monospace;
font-size: 100%;
background: #cad3ea;
}
.readmore {
border: 1px dotted #777;
padding: 3px;
}
.code {
display: block;
white-space: pre;
padding: 6px;
margin: 12px;
}
.embed-code-dl {
font-family: sans-serif;
font-size: 90%
}
input[type=text],input[type=password],textarea {
background : #ebf1ff !important;
color : #000;
border : 1px solid #8c9bc5;
padding : 2px;
}
textarea {
width: 95%;
}
thead, th {
background: #4b6bc5 !important;
color: #fff;
}
.vote, .reputation {
font-family: sans-serif;
font-size : 8pt !important;
background : #ebf1ff;
color: inherit;
}
.post_head {
/* background: #4b6bc5 !important; */
background: #ccc !important;
color: #eee;
}
/* .post_head a {
color: #fff;
background: transparent;
} */
/** replies **/
#replies_table {
background: #4b6bc5;
font-size : 95% !important;
}
.reply-new-body, .reply-new-body p {
font-size: 10pt !important;
}
.pmsig {
border-top: 1px solid #cad3ea;
}
ins {
border: 1px dotted #4b6bc5;
border-top: 0 !important;
text-decoration: none;
}
del {
background: transparent;
color: #999;
border: 1px dotted #999;
border-bottom: 0 !important;
text-decoration: none;
}
/*** Nodelets ***/
.nodelet_container {
font-size: 95%;
font-family: sans-serif;
width: 100%;
}
.nodelet_container, .nodelets, .nodelet_body_row, .nodebody, .nodebody td, .nodebody tr {
background: #cad3ea !important;
}
.nodelets {
border-right: 1px solid #4b6bc5;
}
.nodelet_head_row, .nodehead {
background: #4b6bc5 !important;
color: #fff;
}
/** Chatterbox **/
.chat {
font-family: sans-serif;
font-size : 90%;
}
td.highlight {
background : #ebf1ff;
color : inherit;
}
tr.highlight {
background : #bbb;
color : inherit;
font-family: sans-serif;
}
</code>
-->
</div>
<P>This document is a primer guide to using inside-out objects via
[cpan://Class::InsideOut]. It is intended as a compliment to, not a
replacement of, the documentation provided with that manual.</P>
<H2>Who should read this primer?</H2>
<P>This primer intends to address Perl programmers who are already
familiar with using and creating object classes in Perl, and who wish
to learn about and implement the inside-out class pattern using the
[cpan://Class::InsideOut] module.</P>
<H2>Introduction to the inside-out class pattern</H2>
<P>Anyone familiar with object class development in Perl is familiar
with the traditional pattern for a Perl object class: a single,
blessed hash reference. Each object instance is its own hash
reference with the same data structure.</P>
<P>This approach is simple, and it works well; however, it has
certain limitations. For example, anyone instantiating an object can
bypass the accessors you so carefully constructed and muck about with
the data in the object – after all, the object is just a hash
reference like any other.</P>
<P>Besides that, how often have you accidentally wound up with a
hard-to-find bug in your object class because you've mistyped the
name of a hash key? The interpreter certainly doesn't warn you: it
just auto-vivifies a new attribute with the mistyped name.</P>
<P>The inside-out class pattern addresses these issues (and some
other more esoteric ones) by figuratively turning the idea of a class
“inside-out”. Whereas a traditional object instance is a
blessed reference to a hash whose keys are the names of attributes,
an inside-out object instance is a common key to several class-local
hashes that represent the attributes. That is, an object class
defines a hash for each attribute, and an instance is the key that
points to one element in that hash. By way of example:</P>
<code>
# Accessing an attribute inside a traditional object
$self->{ attribute } = $value;
# Accessing an attribute inside an inside-out object
$attribute { id $self } = $value;
</code>
<P>
This may be a little hard to wrap your head around at first, but as
you continue this primer, it will become more apparent.</P>
<H2>Building your first inside-out class</H2>
<P>When building any class, one has to first decide what attributes
need to be present, and what methods will act on those attributes.
For an inside-out class, we'll also need to decide which of the
attributes are <I>public</I>, <I>read-only</I>, or <I>private.</I></P>
<P>A <I>public</I> attribute can be both
read and modified by any program or module that instantiates the
object (any <I>instantiator</I>.</P>
<P>A <I>read-only</I> attribute can be
read by any instantiator, but modified only internally by the class
(and its children, which we'll get to later).</P>
<P>A <I>private</I> attribute is not
available to the instantiator, only to the class itself (an, again,
it's children).</P>
<P>Let's create a class to represent
a person in an organization. We'll need to know the person's given
name, family name, date of birth, phone number, Social Security
Number, and position within the organization. Some of these
attributes shouldn't be changed by the instantiator, and Social
Security Number should be kept private – it's only used
internally as a unique identifier.</P>
<P>Now, let's look at the same class, declared with the inside-out
pattern and using [cpan://Class::InsideOut]:</P>
<code>
package My::Person;
use Class::InsideOut qw[public readonly private register id];
# declare the attributes
readonly given_name => my %given_name;
readonly family_name => my %family_name;
public birthday => my %birthday;
public phone => my %phone;
private ssn => my %ssn;
public position => my %position;
# object constructor
sub new {
my $self = shift;
register( $self );
$given_name{ id $self } = shift;
$family_name{ id $self } = shift;
$birthday{ id $self } = shift;
$phone{ id $self } = shift;
$ssn{ id $self } = shift;
$position{ id $self } = shift;
}
</code>
<P>
The first item of interest is that the attributes are declared in the
class' package scope, rather than per-instance. Notice that attribute
setting/getting inside methods appears “inside-out”.</P>
<H2>Using an inside-out class</H2>
<P>From the instantiators' point of view, using an inside-out class
is no different than using a regular class:</P>
<code>
use My::Person;
my $manager = My::Person->new(
'Random', 'Hacker',
'12/18/1987',
'555-1212',
'CEO'
);
print "Old phone is: ",$manager->phone();
$manager->phone('555-1313'); #set new phone number
print "New phone is: ",$manager->phone();
</code>
<P>Note that by declaring the attribute 'phone' as public,
[cpan://Class::InsideOut] automatically creates a method that works
as an accessor when called without parameters, and as a mutator when
called with a parameter.</P>
<P>The major difference instatiators will notice is that the
following code will <I>not</I><SPAN STYLE="font-style: normal"> work
in an inside-out class (it would in a traditional class):</SPAN></P>
<code>
$manager->{phone} = '555-1313';
</code>
<P>{{what is the behavior of this??}}</P>
<H2>Defining accessors and mutators</H2>
<P>As we've seen above, there's no need to explicitly define
accessors and mutators, because [cpan://Class::InsideOut] does it for
you. The syntax for this definition is:</P>
<code>
protection accessor_name => my %attribute_variable;
</code>
<P>An accessor method will be created for any <I>public</I> or <I>readonly</I> attribute;
in the case of a <I>public</I> attribute, the accessor will function as a mutator when it is passed
a parameter. For example:</P>
<code>
# $obj->public_attrib() accessor, $obj->public_attrib($value) mutator
public public_attrib => my %public_attrib;
# $obj->read_attrib() accessor, no mutator
readonly read_attrib => my %read_attrib;
# no accessor or mutator
private priv_attrib => my %priv_attrib;
</code>
<P>Notice that throughout this document, the accessor name and the the
name of the attribute variable are the same. While this is not
strictly required, it makes things so much clearer that it's
considered a best practice.</P>
<H2>Defining other types of methods</H2>
<P>Creating methods that act on data inside an object is not entirely
dissimilar from the traditional class pattern approach:</P>
<code>
sub sendPhoneToDirectory {
# sends the phone number to the company directory, using SSN as
# the key
my $self = shift;
my $dir = My::Directory->new();
my $entry = $dir->getEngtryBySSN( $ssn{ id $self } );
$entry->phone( $phone{ id $self } );
$entry->commit;
}
</code>
<P>The only real difference is how attributes are addressed. Instead of
using '$self' as a hash reference and the attribute name as a key, we
use the attribute hash and the 'id' of '$self' (not the object
itself, but its unique identifier) as the key.</P>
<H2>Advanced accessor and mutator behavior</H2>
<P>While [cpan://Class::InsideOut]'s ability to automatically
generate the accessors/mutators for read-only and public attributes
is certainly convenient, it does raise a fairly obvious question:
what happens if I want my accessor/mutator to do a little more than
just get or set the value of the attribute?</P>
<P>It's generally not advisable to have excessive amounts of logic
around getting or setting a value, but there are plenty of places
where a little bit of logic is appropriate. The most obvious example
for a mutator is the desire to validate the proposed value before
setting the attribute. For instance, it wouldn't do to allow a
floating-point value to be assigned to an attribute that's supposed
to be an integer. For accessors, the most obvious example would be
dereferencing a data structure before returning it.</P>
<P>Fortunately, [cpan://Class::InsideOut] has this facility. When
declaring attributes, two hooks are available. The first, for
hooking into the accessor behavior, is called 'get_hook'; its
companion, 'set_hook', hooks into mutator behavior, when that's
relevant.</P>
<P>These hooks are code references that will be executed either
before setting or after getting the attribute, but before the
accessor or mutator returns. The return value is ignored, and the
'$_' variable is locally aliased so that it contains the value in the
attribute (in the case of an accessor) or the value to be assigned
(in the case of a mutator).</P>
<P>An attribute can have either a set_hook or a get_hook, or both.</P>
<H3>Building a set_hook</H3>
<P>A set_hook is a subroutine used to validate or modify the value
passed to an attribute mutator before setting the attribute. Inside a
set_hook, the variable '$_' is set up to be the value passed to the
mutator. Return values are ignored – this means that one
changes '$_' if one wants to modify the passed value, and dies if one
wants to fail (e.g. if bad data were passed to the mutator).</P>
<P>By way of example, imagine that our My::Person class wants to
store the birthday as an integer similar to what would be returned by
Perl's 'time' function. However, we want to accept MM/DD/YYYY as
input. We'll use the [cpan://POSIX] mktime() function to actually
make the conversion. Because this is an example, we'll be assuming
that anything that looks like a date, is; in a production
environment, you'd want your validation to be much more robust.
Here's the sub:</P>
<code>
sub _set_birthday {
# remember, $_ will contain the value passed to the mutator
die "$_ is not a valid date" unless m{(\d{2})/(\d{2})/(\d{4})};
$_ = mktime(0,0,0,$2-1,$1-1,$3-1900); #pack it!
}
</code>
<P>Now, we have to register this subroutine with the set_hook for the
'phone' attribute. This is done at declaration:</P>
<code>
public birthday => my %birthday, { set_hook =>; \&_set_birthday };
</code>
<P>Now, any time an instantiator calls the 'birthy' mutator, the value
will be converted. If the value doesn't look like a date, the
mutator dies. Note that any code reference will be executed, so
there's no need to use a named sub: the 'set_hook' could just as
easily be an anonymous sub.
</P>
<H3>Building a get_hook</H3>
<P>A get_hook is a subroutine used to modify the value returned by an
attribute accessor. Inside a get_hook, the variable '$_' is set up
to be the value stored in the attribute. Return values from get_hook
are ignored; the accessor expects that get_hook will simply modify
'$_'.</P>
<P>Let's continue our birthday example from the 'set_hook'
discussion. Recall that the birthday is stored as a time code in the
same form as would be generated by Perl's 'time' function. However,
since we accept MM/DD/YYYY as input to the mutator, we should provide
the same format as output from the accessor. We'll use the
[cpan://POSIX] strftime() function to do the heavy lifting. Here's
the sub:</P>
<code>
sub _get_birthday {
# remember, $_ will contain the value of the attribute
$_ = strftime '%m/%d/%Y', localtime( $_ );
}
</code>
<P>As with a 'set_hook', we register the get_hook at declaration time.
Here's what the declaration looks like after adding the get_hook:</P>
<code>
public birthday => my %birthday, {
set_hook => \&_set_birthday, # was already here
get_hook => \&_get_birthday,
};
</code><P>
Now, when the birthday accessor is called, it will return the date in
the MM/DD/YYYY format. As with set_hook, an anonymous sub could be
used instead of a separate, named sub.</P>
<H2>Further reading</H2>
<P>Hopefully, this text has been helpful in getting you started
creating your own inside-out modules using [cpan://Class::InsideOut].
It probably won't be long before you find yourself wanting to do
more advanced things using inside-out objects, like serialization,
inheriting from traditional classes, and writing thread-safe modules.
To address these topics, please refer to the excellent
[cpan://Class::InsideOut::Manual::Advanced].</P>
<H2>See also</H2>
<UL>
<LI>[cpan://Class::InsideOut::Manual::About] – the overview
documentation for [cpan://Class::InsideOut]
<LI>[cpan://Object::InsideOut] – a robust inside-out object
base class; a little harder to use than [cpan://Class::InsideOut],
but provides incredible flexibility (including array-based
inside-out objects, if desired).
<LI>[cpan://Class::Std] – a deprecated inside-out object
module; it lacks thread-safety and doesn't reflect best practices,
but it's historically interesting and instructive.
</UL>
<P><BR><BR>
</P>