In Perl, fields are usually not hidden, applying this through the semantics of the language, but rather through a contract in the form of documentation. However, fields can be hidden using closures. It is also worth noting that Perl does not semantically distinguish between class methods and instance methods.
One of the standard ways to implement objects is with a blessed hash, just like you. This hash contains all instance variables / fields. Usually, "private" fields begin with underscores. Usually, the contract (documentation) will not indicate how these fields are stored, but requires that the class user go through various method calls.
Class variables should not be stored in an instance. It is better to use global variables or lexical variables. In the code you specified, $count is just a counter, but you never access it as a class variable. Instead, you assign a unique identifier to each instance. To use it as a class variable, provide the appropriate accessory (I removed unnecessary things like return s):
{ package Base; my $count = 0; sub new { my ($class) = @_; my $self = { ID => $count++, }; bless $self, $class; } sub Count { $count } sub ID { my ($self) = @_; $self->{ID} } sub report { my ($self) = @_; "I am the Base object ".($self->ID)."." } } =head1 Base A generic base class =head2 Base->Count Return the object count. =head2 $base->ID Give the unique ID of this object. =head2 $base->report Returns a string containing a short description. =cut
There is no business intervention with a counter in the subclass. This is ensured by the scope of the variable $count above, denoted by external curly braces. Subjects are closures on this variable.
{ package Sub; use parent -norequire, qw(Base);
As you can see, the Sub constructor calls the Base initializer and then adds a new field. It does not have class methods or class variables. The class does not have access to the $count variable, except for the accessor class method. The contract is indicated through the POD documentation.
(In the Name method, I use the annotation :lvalue , which allows me to simply assign the appropriate field in the object. However, this prohibits argument checking.)
Test
my $base1 = Base->new; my $base2 = Base->new; print "There are now " . Base->Count . " Base objects\n"; my $sub1 = Sub->new; my $sub2 = Sub->new; print "There are now " . Base->Count . " Base objects\n"; $sub2->Name = "Fred"; print $_->report . "\n" for ($base1, $sub1, $base2, $sub2);
prints
There are now 2 Base objects There are now 4 Base objects I am the Base object 0. I am the Sub object 2 called . I am the Base object 1. I am the Sub object 3 called Fred.
Pretty, right? (Except for the $sub1 , this object is missing its name.)
The documentation can be viewed with perldoc -F FILENAME and output something like
Base A generic base class Base->Count Return the object count. $base->ID Give the unique ID of this object. $base->report Returns a string containing a short description. Sub A generic subclass. It subclasses Base. $sub->Name [= SCALAR] Gets or sets the name of $sub. my $oldname = $sub->Name; $sub->name = "new name";
only dials more nicely if you are on a * nix system.
Tested under v5.12.4.
Edit: Objects with an inner outline
While objects with the smallest externality provide the best coding, they are a bad idea: hard to understand, hard to debug, and hard to inherit, they provide more problems than solutions.
{ package Base; my $count = 0; sub new { bless \do{my $o = $count++}, shift } sub Count { $count } sub ID { ${+shift} } sub report { my ($self) = @_; "I am the Base object ".($self->ID)."." } } { package Sub; my @_obj = (); my $count = 0; sub new { my ($class) = @_; $count++; $_obj[$count - 1] = +{ parent => Base->new(), Name => undef, }; bless \do{my $o = $count - 1}, shift; } sub Name :lvalue { $_obj[${+shift}]{Name} } sub AUTOLOAD { my $self = shift; my $package = __PACKAGE__ . "::"; (my $meth = $AUTOLOAD) =~ s/^$package//; $_obj[$$self]{parent}->$meth(@_) } sub report { my ($self) = @_; "I am the Sub object ".($self->ID)." called ".($self->Name)."."; } }
This implementation has the same interface and completes the test case with the same output. This solution is far from optimal, supports only one inheritance, does some intermediate things (autoload, dynamic method calls), but it works amazingly. Each object is actually a reference to an identifier that can be used to find the actual hash containing the fields. An array containing hashes is not accessible from the outside. The Base class has no fields, so an array of objects should not be created.
Edit2: Objects as coderefs
Another bad idea, but it's fun:
{ package Base; my $count = 0; sub new { my ($class) = @_; my $id = $count++; bless sub { my ($field) = @_; die "Undefined field name" unless defined $field; if ($field eq "ID") { return $id } else { die "Unrecognised name $field" } }, $class; } sub Count { $count } sub ID { my ($self) = @_; $self->("ID") } sub report { my ($self) = @_; "I am the Base object " . $self->ID . "." } } { package Sub; use parent -norequire, qw(Base); sub new { my ($class) = @_; my $name = undef; my $super = $class->SUPER::new; bless sub { my ($field, $val ) = @_; die "Undefined field name" unless defined $field; if ($field eq "Name") { defined $val ? $name = $val : $name } else { $super->(@_) } }, $class; } sub Name { my $self = shift; $self->("Name", @_) } sub report { my ($self) = @_; "I am the Sub object ".($self->ID)." called ".($self->Name)."."; } }
The test case should be adapted to $sub2->Name("Fred") , and the corresponding documentation is updated accordingly, since we cannot safely use the lvalue annotation.