Re: Point me in the right direction with OO inheritance and static variables
by choroba (Cardinal) on Sep 02, 2014 at 10:01 UTC
|
In the plain OO approach, just specify the default attributes in the constructor:
sub new {
my $class = shift;
my %params = ( name => 'John',
surname => 'Doe',
);
my %args = (%params, @_); # Overwrite the defaults
bless \%args, $class
}
To call the constructor of the parent class, use SUPER:
sub new {
my $class = shift;
my $self = $class->SUPER::new(%args);
return $self
}
| [reply] [d/l] [select] |
|
|
Thanks! How does the child object "know" which class is the parent? (I hope my terminology is correct)
In other words, when i'm creating an object in the class which is inheriting the attributes, how does it know where SUPER points?
Thanks again for your help!
| [reply] |
|
|
In the child class, you specified
use parent 'Parent::Class';
right? See parent.
| [reply] [d/l] |
|
|
package Child {
require Parent;
our @ISA = "Parent";
}
For some reason, setting @ISA directly like that is not very fashionable, and a lot of people prefer to use a module like base, parent, or superclass to do so. Using one of those modules ensures that @ISA gets set at compile-time rather than run-time, though in practice this feature of them is almost never important.
| [reply] [d/l] [select] |
Re: Point me in the right direction with OO inheritance and static variables
by tobyink (Canon) on Sep 02, 2014 at 10:19 UTC
|
Generally speaking your best bet is to use something like Moose's concept of defaults/builders. Here's an example of how you can implement something very similar using only core modules:
use v5.14; use warnings;
package Horse {
# A little bit of metadata about the class. The constructor
# uses this to find out what attributes the class supports.
sub _attributes {
my $class = shift;
return ( qw( name colour ) );
}
# A fairly standard constructor, similar to MooseX::StrictConstruc
+tor
sub new {
my $class = shift;
my $self = bless {}, $class;
my %params = @_==1 ? %{$_[0]} : @_;
$self->{$_} = delete($params{$_}) for $class->_attributes;
die "Unknown parameter(s)... @{[ sort keys %params ]}"
if keys %params;
return $self;
}
# Accessors for our attributes, with lazy defaults
sub name { $_[0]->{name} //= $_[0]->_build_name }
sub colour { $_[0]->{colour} //= $_[0]->_build_colour }
# Here are the default values for the attributes
sub _build_name { "Cloppity" }
sub _build_colour { "brown" }
# This is for debugging
use JSON::PP;
sub dump {
my $self = shift;
my $class = ref $self;
print JSON::PP->new->pretty(1)->canonical(1)->encode({
__CLASS__ => $class,
map(+($_ => $self->$_), $class->_attributes),
});
}
}
my $ed = Horse->new( name => "Mr Ed" );
$ed->dump;
package Unicorn {
use parent -norequire, "Horse";
# Add "horn" to the list of supported attributes.
sub _attributes {
my $class = shift;
return (
$class->SUPER::_attributes(@_),
qw( horn ),
);
}
# Accessor and default value for "horn" attribute.
sub horn { $_[0]->{horn} //= $_[0]->_build_horn }
sub _build_horn { "medium" }
# override the default colour from Horse
sub _build_colour { "lavender" }
}
my $ts = Unicorn->new( name => "Twilight Sparkle" );
$ts->dump;
# Note that this dies because of the typo!
my $pp = Unicorn->new( naem => "Pinkie Pie" );
| [reply] [d/l] |
|
|
| [reply] [d/l] |
|
|
Wow! Thanks! This is pretty much exactly what i'm thinking of with some minor modification (i wish my program was about Unicorns called Twilight Sparkle!!
I'm having trouble following your subs "name", "colour" etc though.
Could you explain those? I'm guessing that if i attempt to get the value of "name" before i've assigned it a value, it'll call the _build_X sub and so return a default value. But i'm not entirely sure how it does it? Also i've never seen the "//=" operator before.
Thanks!
| [reply] |
|
|
I've written them in quite a terse fashion. This is because when you're doing OO without any OO frameworks, you end up having to write a lot of these sort of little accessor subs, and making them as abbreviated as possible keeps you sane. Writing out the name sub in full might be:
sub name {
my $self = shift;
if (not defined $self->{name}) {
$self->{name} = $self->_build_name();
}
return $self->{name};
}
But if you're writing similar accessors for dozens of different attributes, it's nice to abbreviate them so they fit on a line each:
sub name { $_[0]->{name} //= $_[0]->_build_name }
sub colour { $_[0]->{colour} //= $_[0]->_build_colour }
sub owner { $_[0]->{owner} } # this one has no default
sub height { $_[0]->{height} //= 2.5 } # another way to provide a def
+ault
...;
Slight diversion...
The disadvantage of the second way of doing defaults (shown above) is it makes the default harder to override when you create a subclass. If the height had been defaulted via $_[0]->_build_height then when we decided to write a Pony::Shetland class, we could simply override _build_height to return a different default value (maybe 1.2?). But with the default 2.5 hard-coded into the height sub itself, we need to override height in Pony::Shetland.
Obviously, overriding the height sub in Pony::Shetland is perfectly possible. It's technically no more difficult than overriding _build_height. However, overriding _build_height rather than height seems preferable because OO code tends to be more maintainable when you're only overriding very small targeted bits of functionality.
As an example, let's assume that Pony::Shetland overrides height from Horse. Now somebody goes and releases a new version of Horse with a brand new feature. It allows:
my $aj = Horse->new(name => "Applejack");
my $metres = $aj->height( in => "metres" );
my $inches = $aj->height( in => "inches" );
my $hands = $aj->height( in => "hands" );
my $silly = $aj->height( in => "lightyears" );
Nice piece of new functionality, eh? However, Pony::Shetland overrides height, so the new functionality doesn't work there! There's something called the Liskov substitution principle that says anything that works with the base class should work with subclasses. So we've broken that principle.
If Pony::Shetland was just overriding _build_height, we would never have gotten ourselves into this quandary. The new height would still work in Pony::Shetland.
End of slight diversion!
Regarding //=... the // and //= operators were introduced in Perl 5.10. // is much the same as || but rather than testing the truthiness of the left hand value, it tests the definedness. The number 0 and the empty string are defined but false, so if you wanted to be able to have horses with a name "0", this distinction could be important.
$foo //= $bar is shorthand for $foo = ($foo // $bar), so it means the same as if (not defined $foo) { $foo = $bar }.
| [reply] [d/l] [select] |
|
|
|
|
|
|
|
Re: Point me in the right direction with OO inheritance and static variables
by 2teez (Vicar) on Sep 02, 2014 at 11:15 UTC
|
hi Amblikai,
If what choroba and tobyink gave are too strong a drink to take all at once, you can check this soft milk
use warnings;
use strict;
package Animal;
sub new {
my $class = shift;
my $self = {
'name' => shift || 'Ologbo',
'tail' => shift || 'Yes',
};
return bless $self, $class;
}
package Cat;
use base 'Animal';
sub nameMe {
my $self = shift;
return $self->{'name'};
}
package main;
my $cat = Animal->new;
print $cat->{'name'}, $/; # Ologbo
my $cat2 = Cat->new('Blackky');
print $cat2->nameMe # Blackky
If you tell me, I'll forget.
If you show me, I'll remember.
if you involve me, I'll understand.
--- Author unknown to me
| [reply] [d/l] |
|
|
Hi 2teez,
I'm not really happy with your suggestion for a newcomer. Why?
1) Choroba presented a simple constructor (as strong as pure water to stay with your metaphor) where you can can overwrite the default values of name and tail independently in a way most people would expect working with constructors in Perl. The positional parameters to new you present is not very common IMHO. So, I think Corion's constructor is really simple enough.
2) In package main you then do a
print $cat->{'name'}, $/;
which means you circumvent encapsulation (yes, I know, for debugging purposes). You show that to a newcommer to whom should be taught the "right" thing.
3) The point above IMHO comes only from the fact that you define the method nameMe in the child class. Why haven't you defined it in the base class? Then you wouldn't have to break encapsulation in your example and you would have shown inheritance in a nice mini example.
Be sure, I wouldn't have written this comment when Amblikai wouldn't have been so "excited" about your example. Please consider my comment as a hopefully constructive criticism.
And yes, Tobyink's answer is a hard drink to newcomers, but worth studying.
Best regards
McA
| [reply] [d/l] [select] |
|
|
| [reply] |
Re: Point me in the right direction with OO inheritance and static variables
by Anonymous Monk on Sep 02, 2014 at 10:52 UTC
|
One important thing to realize is that in Perl, only methods are inherited. In other languages it's different, but Perl has no notion of 'object attributes', strictly speaking. Any reference can be an object, including references to numbers and strings (and these things can't have user-defined attributes).
I'm not sure where to begin with inheritance though. I was hoping someone could point me in the right direction?
A package in Perl can have a magic array @ISA. This array can hold strings, which must be names of packages (class and package is the same thing).
When you call a method on an object, like
$some_object->some_method()
Perl checks which class (package) this $some_object belongs to (you specified it with function bless). If that class doesn't have some_method, then Perl checks the @ISA array of the class. It takes the first string from this array, assumes it's a package name and then searches that package for some_method. If some_method is found, Perl calls some_method, with $some_object as the first argument. So, $some_object->some_method() is equivalent to SomeClass::some_method($some_object) (SomeClass comes from an @ISA array). And that is OOP in Perl. | [reply] [d/l] [select] |
|
|
and these things can't have user-defined attributes
Unless inside out.
| [reply] |
|
|
BrowserUK came up with an inside-in storage technique using blessed references to strings.
Tends to be a little slow for attribute access, but the objects take up very little memory, which is nice.
a href=
| [reply] |
Re: Point me in the right direction with OO inheritance and static variables
by GrandFather (Saint) on Sep 03, 2014 at 01:14 UTC
|
| [reply] |
| A reply falls below the community's threshold of quality. You may see it by logging in. |