Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options

Re^6: Perl "new" command

by JavaFan (Canon)
on Mar 01, 2012 at 12:51 UTC ( #957201=note: print w/replies, xml ) Need Help??

in reply to Re^5: Perl "new" command
in thread Perl "new" command

Perl leaves to the programmer the task of allocating the object storage, setting the dispatch table and initializing the object for greater flexibility.
In theory.

In practice, the overwhelming majority of the programmers screws this up, each and every time they create a class. I bet there are seasoned Perl programmers who haven't managed not not screw up.


package Colour; sub new {bless {colour => $_[1]}, $_[0]} sub colour {$_[0]{colour}} package Age; sub new {bless {age => $_[1]}, $_[0]}} sub age {$_[0]{age}}
Whee. Now I can make colour objects, and age objects:
use Colour; use Age; my $c_obj = Colour::->new("orange"); my $a_obj = Age::->new(42); print "I have an object with colour ", $c_obj->colour; print "And another object aged ", $a_obj->age;
Goodie. Now, if there was any flexibility, I'd be able to create a class that's both, using multiple inheritance, and without breaking encapsulation (that is, peeking and making use of the implementation of a parent class):
package Age_and_Colour; use Age; use Colour; our @ISA = qw[Age Colour]; sub new { my ($class, $age, $colour) = @_: ... Now what? ... }
If I call Age::->new, I get back an object initialized with an age, but no colour; but if I call Colour::->new, I get one with a colour, but no age.

Greater flexibility is fine, but if this requires a lot more effort from the programmer, it's pointless. That's like saying C has greater flexibility when it comes to regular expressions: it gives you a minimal string implementation, and leaves everything else to the programmer. Sure, you *can* make more fantastic regular expressions in C than in Perl, but I don't see many people actually doing that.

(cue the Pavlov reactions: "but you shouldn't use MI anyway")

Replies are listed 'Best First'.
Re^7: Perl "new" command
by tobyink (Abbot) on Mar 01, 2012 at 13:42 UTC
    ... Now what? ...

    can be replaced by:

    bless do { my %self = (%{Colour->new($colour)}, %{Age->new($age)}); \% +self }, $class;

    Moose (and some of its imitators) thankfully handles all of this for you. The Moose philosophy is to never write your own new method - rely on Moose to take care of it for you. If you do need to do some initialisation, then create a BUILD method - no matter how complex the inheritance you're using, Moose should call all the BUILD methods in a sane order.

    The other solution is to go the inside-out object route. If your class doesn't care about the contents of the reference, then all is fine and dandy. The reference doesn't even have to be a hash/array any more - it could be a scalarref, a coderef, a quoted regexp, etc. Inside-out objects have been traditionally fiddly to write without leaking memory, but the recent "fieldhashes" modules have made it a lot easier.

      bless do { my %self = (%{Colour->new($colour)}, %{Age->new($age)}); \% +self }, $class;
      So, you break encapsulation, and will depend on how things are implemented. If you're willing to break encapsulation, why even bother with OO?
      The other solution is to go the inside-out object route. If your class doesn't care about the contents of the reference, then all is fine and dandy.
      Inside-out objects aren't a silver bullet, and won't save the naive programmer.
      package Colour { use Scalar::Util 'refaddr'; my %colour; sub new { my $o = bless \do{my $var}, $_[0]; $colour{refaddr $o} = $_[1]; $o; } sub colour {$colour{refaddr $_[0]}} }; package Age { use Scalar::Util 'refaddr'; my %age; sub new { my $o = bless \do{my $var}, $_[0]; $age{refaddr $o} = $_[1]; $o; } sub age {$age{refaddr $_[0]}} }; package Colour_and_Age { our @ISA = qw[Age Colour]; use Scalar::Util 'refaddr'; sub new { my ($class, $colour, $age) = @_; ... Now what? ... } }
      Packages Colour and Age are written as inside-out objects, but because the same method constructs and initializes the object, the author of Colour_and_Age is screwed. He doesn't even have the luxury of breaking encapsulation.

      Now, if one separates object construction and object initialization, for instance:

      use Scalar::Util 'refaddr'; package Colour { use Scalar::Util 'refaddr'; my %colour; sub new {bless \do{my $var}, $_[0]} sub init {$colour{refaddr $_[0]} = $_[1]; $_[0]} sub colour {$colour{refaddr $_[0]}} }; package Age { use Scalar::Util 'refaddr'; my %age; sub new {bless \do{my $var}, $_[0]} sub init {$age{refaddr $_[0]} = $_[1]; $_[0]} sub age {$age{refaddr $_[0]}} };
      then one can use multiple inheritance without any problem:
      package Colour_and_Age { use Scalar::Util 'refaddr'; our @ISA = qw[Age Colour]; sub new {bless \do{my $var}, $_[0]} sub init ( my ($self, $colour, $age) = @_; $self->Colour::init($colour); $self->Age::init($age); $self; } }; my $o = Colour_and_Age::->new->init(red => 42);
      It only takes a few more keystrokes to create your classes that way, and you aren't robbing any users from using your classes with multiple inheritance. Also note you don't need inside-out objects to separate constructions and initialization.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://957201]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (1)
As of 2018-10-22 03:06 GMT
Find Nodes?
    Voting Booth?
    When I need money for a bigger acquisition, I usually ...

    Results (119 votes). Check out past polls.