How to make "universal" getters and setters in an object in perl?

accessor, getter, getter-setter, perl, setter

Solution

This is not a good idea.

Perl instituted the use of `use strict;` to take care of problems like this:

 $employee_name = "Bob";

 print "The name of the employee is $employeeName\n";

Mistyped variable names were a common problem. Using `use strict;` forces you to declare your variable, so errors like this can be caught at compile time.

However, hash keys and hash references remove this protection. Thus:

my $employee[0] = {}

$employee[0]->{NAME} = "Bob";

print "The name of the employee is " . $employee[0]->{name} . "\n";

One of the reasons to use objects when you start talking about complex data structures is to prevent these types of errors:

 my $employee = Employee->new;
 $employee->name("Bob");
 print "The name of the employee is " . $employee->Name . "\n";

This error will get caught because the method name is `name` and not `Name`.

Allowing users to create their own methods at random removes the very protection we get by using objects:

 my $employee = Employee->new;
 $employee->name("Bob");    #Automatic Setter/Getter
 print "The name of the employee is " . $employee->Name . "\n";  #Automatic Setter/Getter

Now, because of automatic setters and getters, we fail to catch the error because any method the user names is valid -- even if that user made a mistake.

In fact, I setup my objects so my object doesn't necessarily know how it's structured. Observe the following class with methods `foo` and `bar`:

 sub new {
    my $class = shift;
    my $foo   = shift;
    my $bar   = shift;

    my $self = {};
    bless $self, $class;

    $self->foo($foo);
    $self->bar($bar);
    return $self;

}

sub foo {
    my $self = shift;
    my $foo  = shift;

    my $method_key = "FOO_FOO_FOO_BARRU";

    if (defined $foo) {
        $self->{$method_key} = $foo;
    }
    return $self->{$method_key};
 }

sub bar {
    my $self = shift;
    my $bar  = shift;

    my $method_key = "BAR_BAR_BAR_BANNEL";

    if (defined $bar) {
        $self->{$method_key} = $bar;
    }
    return $self->{$method_key};
}

I can set the class values for `foo` and `bar` in my constructor. However, my constructor doesn't know how those values are stored. It simply creates the object and passes it along to my getter/setter methods. Nor, do my two methods know how they store each other's value. That's why I can have such crazy names for my method's hash keys because that is only available in the method and no where else.

Instead, my methods `foo` and `bar` are both setters and getters. If I give them a value for `foo` or `bar`, that value is set. Otherwise, I simply return the current value.

However, I'm sure you already know all of this and will insist this must be done. Very well...

One way of handling what you want to do is to create an AUTOLOAD subroutine. The AUTOLOAD subroutine automatically is called when there's no other method subroutine by that name. The `$AUTOLOAD` contains the class and method called. You can use this to setup your own values.

Here's my test program. I use two methods `bar` and `foo`, but I could use any methods I like and it would still work fine

One change, I use a parameter hash in my constructor instead of a list of values. No real difference except this is considered the modern way, and I just want to be consistent with what everyone else does.

Also notice that I normalize my method names to all lowercase. That way `$object->Foo`, `$object->foo`, and `$object-FOO` are all the same method. This way, I at least eliminate capitalization errors.

use strict;
use warnings;
use feature qw(say);
use Data::Dumper;

my $object = Foo->new({ -bar => "BAR_BAR",
        -foo => "FOO_FOO",
    }
);

say "Foo: " . $object->foo;
say "Bar: " . $object->bar;

$object->bar("barfu");
say "Bar: " . $object->bar;

say Dumper $object;


package Foo;

    sub new {
    my $class =      shift;
    my $param_ref  = shift;

    my $self = {};
    bless $self, $class;

    foreach my $key (keys %{$param_ref}) {

        # May or may not be a leading dash or dashes: Remove them
        (my $method = $key) =~ s/^-+//;

        $self->{$method} = $param_ref->{$key};
    }
    return $self;
}


sub AUTOLOAD {
    my $self  = shift;
    my $value = shift;

    our $AUTOLOAD;

    ( my $method = lc $AUTOLOAD ) =~ s/.*:://;

    if ($value) {
        $self->{$method} = $value;
    }
    return $self->{$method};
}

Problem

How do you make one setter method, and one getter method to manage access to fields of an object? The new subroutine looks like this: ``` sub new { my $class = shift; my $self = {@_}; bless($self,$class); # turns this into an object } ``` Creation of a new object looks like this: ``` $foo = Package::new("Package", "bar", $currentBar, "baz", $currentBaz, ); ```

Original source