NAME

Object::Simple - Generate accessor having default value, and provide constructor

SYNOPSIS

Class definition.

package SomeClass;

use base 'Object::Simple';

# Generate a accessor
__PACKAGE__->attr('foo');

# Generate a accessor having default value
__PACKAGE__->attr(foo => 1);
__PACKAGE__->attr(foo => sub { [] });
__PACKAGE__->attr(foo => sub { {} });
__PACKAGE__->attr(foo => sub { OtherClass->new });

# Generate accessors at once
__PACKAGE__->attr([qw/foo bar baz/]);
__PACKAGE__->attr([qw/foo bar baz/] => 0);

Use the class

# Constructor
my $obj = SomeClass->new;
my $obj = SomeClass->new(foo => 1, bar => 2);
my $obj = SomeClass->new({foo => 1, bar => 2});

# Get the value
my $foo = $obj->foo;

# Set the value
$obj->foo(1);

Class accessor.

# Generate class accessor
__PACKAGE__->class_attr('foo');

# Generate inheritable class accessor
__PACKAGE__->class_attr('foo', inherit => 'scalar_copy');
__PACKAGE__->class_attr('foo', inherit => 'array_copy');
__PACKAGE__->class_attr('foo', inherit => 'hash_copy');

Dual accessor for both the object and its class

# Generate dual accessor
__PACKAGE__->dual_attr('foo');

# Generate inheritable dual accessor
__PACKAGE__->dual_attr('foo', inherit => 'scalar_copy');
__PACKAGE__->dual_attr('foo', inherit => 'array_copy');
__PACKAGE__->dual_attr('foo', inherit => 'hash_copy');

DESCRIPTION

1. Features

Object::Simple is a generator of accessor, such as Class::Accessor, Mojo::Base, or Moose. Class::Accessor is simple, but lack offten used features. new() method can't receive hash arguments. Default value can't be specified to the accessor. If multipule values is set through the accssor, its value is converted to array reference without warnings.

Moose is so complex for many people to use, and depend on many modules. This is almost another language, not fit familiar perl syntax. Moose increase the complexity of projects, rather than increase production efficiency. In addition, its complie speed is slow and used memroy is huge.

Object::Simple is the middle area between Class::Accessor and complex class builder. Only offten used features is implemnted. new() can receive hash or hash reference as arguments. Default value is specified for the accessor. Compile speed is fast and used memory is small. Debugging is natural because the code is easy to read. Object::Simple is compatible of Mojo::Base for some people to use easily.

In addition, Object::Simple can generate class accessor , and dual accessor for both the object and its class. This is like Class::Data::Inheritable, but more flexible, because You can specify the way to copy the value of super class.

2. Generate accessor

At first, you create a class extending Object::Simple to use methods of Object::Simple.

package SomeClass;

use base 'Object::Simple';

Object::Simple have new() method to create a new object. it can receive hash and hash reference as arguments.

my $obj = SomeClass->new;
my $obj = SomeClass->new(foo => 1, bar => 2);
my $obj = SomeClass->new({foo => 1, bar => 2});

You can generate accessor by attr() method.

__PACKAGE__->attr('foo');

You can set and get the value by accessor.

# Set the value
$obj->foo(1);

# Get the value
my $foo = $obj->foo;

You can specify a default value for the accessor.

__PACKAGE__->attr(foo => 1);

If the value of foo is not exists and foo() is called, You can get the default value.

my $default_value = $obj->foo;

If you want to specify a reference or object as default value, it must be sub reference, whose return value is the default value. This is requirment not to share the default value with other objects.

__PACKAGE__->attr(foo => sub { [] });
__PACKAGE__->attr(foo => sub { {} });
__PACKAGE__->attr(foo => sub { SomeClass->new });

You can generate accessors at once.

__PACKAGE__->attr([qw/foo bar baz/]);
__PACKAGE__->attr([qw/foo bar baz/] => 0);

Example:

I show a example to understand Object::Simple well.

Point class, which have two attribute, x and y, and clear() method to set x and y to 0.

package Point;

use strict;
use warnings;

use base 'Object::Simple';

__PACKAGE__->attr(x => 0);
__PACKAGE__->attr(y => 0);

sub clear {
    my $self = shift;
    
    $self->x(0);
    $self->y(0);
}

Point3D class, which inherit Point class. This class has z attribute in addition to x and y. clear() method is overridden to clear x, y and z.

package Point3D;

use strict;
use warnings;

use base 'Point';

__PACKAGE__->attr(z => 0);

sub clear {
    my $self = shift;
    
    $self->SUPER::clear();
    
    $self->z(0);
}

3. Concepts of Object-Oriented programing

Inheritance

I explain the essence of Object-Oriented programing to use Object::Simple well.

First concept of Object-Oriented programing is Inheritance. Inheritance means that If Class B inherit Class A, Class B can call all method of class A.

+---+
| A | Base class
+---+   having metho1() and method2()
  |
+---+
| B | Sub class
+---+   having method3()

Class B inherits Class A, so B can call all methods of A in addition to methods of B. In other words, B can call method1(), method2(), and method3()

use base module to inherit a class.

package A;

sub method1 { ... }
sub method2 { ... }

package B;

use base 'A';

sub method3 { ... }

Perl has useful functions and methods to help Object-Oriented programing.

ref() function to know the object is belong to what class.

my $class = ref $obj;

isa() method to know whether the object inherits the specified class.

$obj->isa('SomeClass');

can() method to know whether the object(or class) can call the specified method.

SomeClass->can('method1');
$obj->can('method1');

Capsulation

Second concept is capsulation. Capsulation means that you don't touch internal data directory. You must use public methods written in documentation. If you keep this rule, All the things become simple and secure.

Use only accessor to get and set to the attribute value.

my $value = $obj->foo;
$obj->foo(1);

It is bad manner to access the value directory.

my $value = $obj->{foo};
$obj->{foo} = 1;

Polymorphism

Third concept is polymorphism. Polymorphism is devieded into two concepts, overloading> and overriding.

Perl programer don't have to care overloading. Perl is dynamic language, so subroutine can receive any value. Overloading is worth for languages having static type variable, like C++ or Java.

Overriding means that you can change base class's methods in sub class.

package A;

sub method1 { return 1 }

package B;

use base 'A';

sub method1 { return 2 }

method1() of class A return 1. method1() of class B return 2. That is to say, method1() is overridden in class B.

my $obj_a = A->new;
$obj_a->method1; # return value is 1

my $obj_b = B->new;
$obj_b->method1; # return value is 2

If you want to call the method of super class from sub class, use SUPER pseudo-class.

package B;

sub method1 {
    my $self = shift;
    
    my $value = $self->SUPER::method1(); # return value is 1
    
    return 2 + $value;
}

If you understand only these three concepts, you can do enough and powerful Object-Oriented programing. and source code is understandable for any language users.

4. Special accessors

Class accessor

You sometimes want to save the value to class, not object.

my $foo = SomeClass->foo;
SomeClass->foo(1);

use class_attr() method if you want to create accessor for class variable.

__PACKAGE__->class_attr('foo');

You can also specify default value as same as attr() method. See section "METHODS class_attr".

The value is saved to $CLASS_ATTRS. this is hash reference. "SomeClass->foo(1)" is same as the follwoing one.

$SomeClass::CLASS_ATTRS->{foo} = 1;

If the value is set through sub class, the value is saved to sub class, not base class.

Base class.

package BaseClass;

__PACKAGE__->class_attr('foo');

Sub class.

package SubClass;

use base 'BaseClass';

Call foo() in sub class

SubClass->foo(1);

This is same as

$SubClass::CLASS_ATTRS->{foo} = 1;

If you want to inherit the value of base class, use inherit option.

__PACKAGE__->class_attr('foo', inherit => 'scalar_copy');

The value of inherit option is the way to copy the value.

  • scalar_copy - normal copy

    my $copy = $value;
  • array_copy - surface array copy

    my $copy = [@{$value}];
  • hash_copy - surface hash copy

    my $copy = {%{$value}};

Dual accessor

Dual accessor is the accessor having the features of normal accessor and class accessor.

my $foo = $obj->foo;
$obj    = $obj->foo(1);

my $foo = SomeClass->foo;
$class  = SomeClass->foo(1);

If the value is set through the object, the value is saved to the object. If the value is set through the class, the value is saved to the class.

use dual_attr() method to generate dual accessor.

__PACKAGE__->dual_attr('foo');

You can also specify the default value. as same as attr() method. See also section "METHODS dual_attr".

dual_attr() method have inherit option as same as class_attr(), but one point is difference. If you try to get the value through the object, the value is inherited from the class.

+------------+
| Some class |
+------------+
      |
      v
+------------+
|    $obj    |
+------------+

Source code.

SomeClass->foo(1);
my $obj = SomeClass->new;
my $value_of_some_class = $obj->foo;

This can be chained from base class.

+------------+
| Base class |
+------------+
      |
      v
+------------+
| Some class |
+------------+
      |
      v
+------------+
|    $obj    |
+------------+

Base class.

package BaseClass;

__PACKAGE__->dual_attr('foo', inherit => 'scalar_copy');

Sub class.

package SubClass;

use base 'BaseClass';

The value is inherited from the base class.

BaseClass->foo(1);
my $obj = SubClass->new;
my $value_of_base_class = $obj->foo;

This technique is explained again in section "5. Offten used techniques - Inherit a value from class to object"

5. Offten used techniques

Override new() method

new() method is overridden if needed.

Example:

Initialize the object

sub new {
    my $self = shift->SUPER::new(@_);
    
    # Initialization
    
    return $self;
}

Example:

Change arguments of new().

sub new {
    my $self = shift;
    
    $self->SUPER::new(x => $_[0], y => $_[1]);
    
    return $self;
}

You can pass array to new() method by overridden new() method.

my $point = Point->new(4, 5);

Inherit the value from class to object

If you want to save the value to the class and get the value from the object, use dual_attr() with default and inherit option.

For example, If you register some functions to the class, and call the functions from the object, create the following class.

package SomeClass;

__PACKAGE__->dual_attr(
  'functions', default => sub { {} }, inherit => 'hash_copy');

sub register_function {
    my $invocant = shift;
    
    my $functions = ref $_[0] eq 'HASH' ? $_[0] : {@_};
    $invocant->functions({%{$invocant->functions}, %$functions});
    
    return $invocant;
}

__PACKAGE__->register_function(
    func1 => sub { return 1 }
);

Fucntions is saved to functions attribute. You can register function by register_function(). Registered functions is called from the object.

my $obj = SomeClass->new;
my $value = $obj->functions->{func1};

This is like "Prototype inheritance of JavaScript", but more flexible. You can also register functions in sub class and object.

Base class.

package BaseClass;

# (Code is same as above SomeClass)

Sub class.

package SubClass;

__PACKAGE->register_function(
    func2 => sub { return 2 }
);

Object.

my $obj = SubClass->new;
$obj->register_function(
    func3 => sub { return 3 }
);

You can call registered functions from the object

my $value1 = $obj->functions->{func1};
my $value2 = $obj->functions->{func2};
my $value3 = $obj->functions->{func3};

Practical example is Validator::Custom and Validator::Custom::HTMLForm. See also these modules.

6. More features

Check arguments

Object::Simple pay attention to usability. If wrong number arguments is passed to new() or accessor, exception is thrown.

# Constructor must receive even number arguments or hash refrence
my $obj = SomeClass->new(1);

# Accessor must receive only one argument
$obj->foo(a => 1);

Import methods

You can import methods of Object::Simple. This is useful in case you don't want to do multiple inheritance.

package SomeClass;

use Object::Simple qw/new attr class_attr dual_attr/;

__PACKAGE__->attr('foo');

Note that you can't override new() method because new() method is not inherited from the base class,

Method chain

Accessor return self-object when it is called to set the value, so you can do method chain.

$obj->foo(1)->bar(4)->baz(6);

METHODS

new

my $obj = Object::Simple->new(foo => 1, bar => 2);
my $obj = Object::Simple->new({foo => 1, bar => 2});

Create a new object. new() method receive hash or hash reference as arguments.

attr

Generate accessor.

__PACKAGE__->attr('foo');
__PACKAGE__->attr([qw/foo bar baz/]);
__PACKAGE__->attr(foo => 1);
__PACKAGE__->attr(foo => sub { {} });

Generate accessor. attr() method receive two arguments, accessor name and default value. If you want to create multipule accessors at once, specify accessor names as array reference at first argument. Default value is optional. If you want to specify refrence or object as default value, the value must be sub reference not to share the value with other objects.

Generated accessor.

my $value = $obj->foo;
$obj      = $obj->foo(1);

You can set and get a value. If a default value is specified and the value is not exists, you can get default value. Accessor return self object if a value is set.

class_attr

__PACKAGE__->class_attr('foo');
__PACKAGE__->class_attr([qw/foo bar baz/]);
__PACKAGE__->class_attr(foo => 1);
__PACKAGE__->class_attr(foo => sub { {} });

Generate accessor for class variable. class_attr() method receive two arguments, accessor name and default value. If you want to create multipule accessors at once, specify accessor names as array reference at first argument. Default value is optional. If you want to specify refrence or object as default value, the value must be sub reference not to share the value with other objects.

Generated class accessor.

my $value = SomeClass->foo;
$class    = SomeClass->foo(1);

You can set and get a value. If a default value is specified and the value is not exists, you can get default value. Accessor return class name if a value is set.

Class accessor save the value to the class variable "CLASS_ATTRS". The following two is same.

SomeClass->foo(1);
$SomeClass::CLASS_ATTRS->{foo} = 1;

You can delete the value and check existence of it.

delete $SomeClass::CLASS_ATTRS->{foo};
exists $SomeClass::CLASS_ATTRS->{foo};

If the value is set from subclass, the value is saved to the class variable of subclass.

$SubClass->foo(1);
$SubClass::CLASS_ATTRS->{foo} = 1;

If you want to inherit the value of super class, use default and inherit options.

__PACKAGE__->class_attr(
    'foo', default => sub { {} }, inherit => 'hash_copy');

you must specify the way to copy the value of super class to inherit option. this is one of scalar_copy, array_copy, hash_copy, or sub reference.

scalar copy is normal copy, array_copy is surface copy of array reference hash_copy is surface copy of hash reference. the implementations are the following ones.

# scalar_copy
my $copy = $value;

# array_copy
my $copy = [@{$value}];

# hash_copy
my $copy = {%{$value}};

dual_attr

__PACKAGE__->dual_attr('foo');
__PACKAGE__->dual_attr([qw/foo bar baz/]);
__PACKAGE__->dual_attr(foo => 1);
__PACKAGE__->dual_attr(foo => sub { {} });

Generate accessor for both object and class variable. dual_attr() method receive two arguments, accessor name and default value. If you want to create multipule accessors at once, specify accessor names as array reference at first argument. Default value is optional. If you want to specify refrence or object as default value, the value must be sub reference not to share the value with other objects.

Generated dual accessor.

my $value = $obj->foo;
$obj      = $obj->foo(1);

my $value = SomeClass->foo;
$class    = SomeClass->foo(1);

You can set and get a value. If a default value is specified and the value is not exists, you can get default value. Accessor return class name or object if a value is set.

If accessor is called from object, the value is saved to object. If accesosr is called from class name, the value is saved to class variable. See also description of class_attr() method.

dual_attr() method have default and inherit options as same as class_attr() method have.

__PACKAGE__->dual_attr(
    'foo', default => sub { {} }, inherit => 'hash_copy');

But one point is different. If accessor is called from a object, the object inherit the value of the class.

SomeClass->foo({name => 1});
my $obj = SomeClass->new;
my $foo = $obj->foo;

$foo is {name = 1}> because it inherit the value of class.

STABILITY

Object::Simple is now stable. APIs and the implementations will not be changed in the future.

BUGS

Please tell me bugs if they are found.

AUTHOR

Yuki Kimoto, <kimoto.yuki at gmail.com>

COPYRIGHT & LICENSE

Copyright 2008 Yuki Kimoto, all rights reserved.

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