OO Perl help for a dot Net convert?

B

ben.wilder

Hello all,

I am making the transition from .Net to Perl for a new project and i
have a few questions about OO Perl, that i would be most grateful if
someone could comment on / answer!

Many thanks for any of your help,


This problem is conceptual, in that i am waiting to learn about certain
features before trying to actually implement this - so i'll put
together a dummy OO module and a calling script.

------------------------------------------------------------------------------------------
****************
* Perl Module *
****************
package MyPackage;


#Use definitions
use strict;


#Contructor for MyPackage Class
sub new {
my ($class, %arg) = @_;
bless {
_myFirstName => $arg{myFirstName},
_myLastName => $arg{myLastName},
_myAge => $arg{myAge},
_myFavouriteArtists => $arg{myFavouriteArtists},
}, $class;
}


sub ShowAgeAndArtists{

my $self = $_[0];

#Dereference array
my @myFavouriteArtists = @{$self->{_myFavouriteArtists}};

my $artist = '';

foreach $artist ( @myFavouriteArtists )
{
print "$self->{_myFirstName} aged $self->{_myAge}, likes $artist\n";
}


}

1;
------------------------------------------------------------------------------------------


------------------------------------------------------------------------------------------
********************
* Calling script *
********************
#!/usr/bin/perl -w

use strict;
use MyPackage;

my $myFirstName = 'Ben';
my $myLastName = 'Wilder';
my $myAge = 28;
my @myFavouriteArtists = qw[Wolfmother LedZep ChesneyHawkes];

#Instantiate MyPackage class
my $myPackage =
MyPackage->new(myFirstName=>$myFirstName,myLastName=>$myLastName,myAge=>$myAge,myFavouriteArtists=>\@myFavouriteArtists);

$myPackage->ShowAgeAndArtists();

------------------------------------------------------------------------------------------

Now that i have my example, i'd like to ask a few questions:

1. I am passing a reference to the @myFavouriteArtists array, however
with objects being in my mind, an encapsulation of data, it this the
most effective way to pass array data to an object? Can i pass the data
by value so that the object deals with its own copy of the data - or am
i being silly?


2. In MS .Net there is the principle of object fields, object instance
scoped, not class scoped, that are not accessible through any public
methods, but are used possibly by methods private to the class. For
example - in the above scenario i may want to implement a boolean flag
"IsTooOld" that can be set by a private internal method for the
purposes of a logical decision elsewhere in the module. How can i
define such a variable in perl? It seems that since i am blessing the
hash into object-hood, that is where i will have to put the definition,
but i dont want the constructor to be able to alter this definition!
Any ideas (please let me know if i havent put explained this well
enough!)


Thanks again for any help!

Now go easy on the perl noob guys *backs into corner*


Ben
 
A

Anno Siegel

Hello all,

I am making the transition from .Net to Perl for a new project and i
have a few questions about OO Perl, that i would be most grateful if
someone could comment on / answer!

Many thanks for any of your help,


This problem is conceptual, in that i am waiting to learn about certain
features before trying to actually implement this - so i'll put
together a dummy OO module and a calling script.

------------------------------------------------------------------------------------------
****************
* Perl Module *
****************
package MyPackage;


#Use definitions
use strict;


#Contructor for MyPackage Class
sub new {
my ($class, %arg) = @_;
bless {
_myFirstName => $arg{myFirstName},
_myLastName => $arg{myLastName},
_myAge => $arg{myAge},
_myFavouriteArtists => $arg{myFavouriteArtists},

Better:

_myFavouriteArtists => [ @{ $arg{myFavouriteArtists} } ],

to decouple the original array (see below).
}, $class;
}


sub ShowAgeAndArtists{

my $self = $_[0];

#Dereference array
my @myFavouriteArtists = @{$self->{_myFavouriteArtists}};

Copying the array is not necessary here, you're not changing it.
my $artist = '';

foreach $artist ( @myFavouriteArtists )

Just say

foreach $artist ( @{ $self->{ _myFavouriteArtists} } ) {
{
print "$self->{_myFirstName} aged $self->{_myAge}, likes $artist\n";
}


}

1;
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
********************
* Calling script *
********************
#!/usr/bin/perl -w

use strict;
use MyPackage;

my $myFirstName = 'Ben';
my $myLastName = 'Wilder';
my $myAge = 28;
my @myFavouriteArtists = qw[Wolfmother LedZep ChesneyHawkes];

#Instantiate MyPackage class
my $myPackage =
MyPackage->new(myFirstName=>$myFirstName,myLastName=>$myLastName,myAge=>$myAge,myFavouriteArtists=>\@myFavouriteArtists);
$myPackage->ShowAgeAndArtists();
------------------------------------------------------------------------------------------

Now that i have my example, i'd like to ask a few questions:

1. I am passing a reference to the @myFavouriteArtists array, however
with objects being in my mind, an encapsulation of data, it this the
most effective way to pass array data to an object? Can i pass the data
by value so that the object deals with its own copy of the data - or am
i being silly?

No, not at all. Storing a reference to the @myFavouriteArtists array
means that later changes to that array will be reflected in the
object -- usually not what you want. To avoid this, make a copy
of the array when you initialize the object (see code above). On
the other hand you're copying the array on access, which is not
necessary.
2. In MS .Net there is the principle of object fields, object instance
scoped, not class scoped, that are not accessible through any public
methods, but are used possibly by methods private to the class. For
example - in the above scenario i may want to implement a boolean flag
"IsTooOld" that can be set by a private internal method for the
purposes of a logical decision elsewhere in the module. How can i
define such a variable in perl? It seems that since i am blessing the
hash into object-hood, that is where i will have to put the definition,
but i dont want the constructor to be able to alter this definition!
Any ideas (please let me know if i havent put explained this well
enough!)

There is no concept of private methods in Perl OO. Instead we use the
simple convention that methods whose names start with an underscore
are meant to be private and the class user is not supposed to call
them directly. This is not enforcable, however.

Two more remarks:

It is usually better to have a separate initializer method (->init, say)
in each class that takes a given object and sets it up for use with
that class. The conventional ->new method can then be written:

sub new {
my $class = shift;
bless( {}, $class)->init( @_);

This separation is not usually taught with Perl OO, but it is
nevertheless important. It is hard to set up objects for multiple
inheritance without it.

You have chosen the common implementation of Perl objects as hashrefs.
It may be worth while to acquaint yourself with the concept of inside-out
classes. These are a relatively newly developed alternative to more
conventional ways of implementing objects in Perl. Inside-out classes
are a little more involved than other types of classes, but give you
a class that can be inherited by *any* other Perl class, whether it
is itself inside-out or not. Normal classes are usually confined in
inheritance to classes of the same type (hash in your case), or else...
Google for "inside-out Perl" with an eye to articles on perlmonks.

Anno
 
P

Peter J. Holzer

Now that i have my example, i'd like to ask a few questions:

1. I am passing a reference to the @myFavouriteArtists array, however
with objects being in my mind, an encapsulation of data, it this the
most effective way to pass array data to an object? Can i pass the data
by value so that the object deals with its own copy of the data - or am
i being silly?

Your constructor needs to distinguish between its arguments. Since there
is exactly one array, you could change your constructor to:

sub new {
my ($class, $myFirstName, $myLastName, $myAge, @myFavouriteArtists) = @_;
...
}

and call it like

MyPackage->new($myFirstName,$myLastName,$myAge,@myFavouriteArtists);

or

MyPackage->new("Ben","Wilder",28, "Wolfmother", "LedZep", "ChesneyHawkes");

But what happens if you add a second array?

Alternatively, you could scan the @_ in the constructor for keywords
like "myFirstName", "myLastName", etc. But what happens if someone
founds a band called "myAge"?

So I think, passing references to arrays is generally the best way.

You can of course make a copy of the array in the constructor:

_myFavouriteArtists => [ @{ $arg{myFavouriteArtists} } ],

if you want to. I found this rarely to be necessary, and usually made
the copy when calling the constructor (because when I'm calling the
constructor, I know whether I will continue to use the array. I don't
know this in the constructor).
2. In MS .Net there is the principle of object fields, object instance
scoped, not class scoped, that are not accessible through any public
methods, but are used possibly by methods private to the class.

This seems to be a rather strange concept to me. I understand making
fields private so they are only accessible through methods from the
outside. But why make them inaccessible from the inside?
For example - in the above scenario i may want to implement a boolean
flag "IsTooOld" that can be set by a private internal method for the
purposes of a logical decision elsewhere in the module. How can i
define such a variable in perl?

Perl doesn't even have private methods, much less a way to declare that
some variable should be accessible to only some methods.

It's probably possible to achieve this with tie. Maybe there is even
already a package on CPAN which does this. (There are a lot of packages
on CPAN implementing different styles of object oriented programming -
I've played with a few but always went back to the basic blessed hashref
style).
It seems that since i am blessing the hash into object-hood, that is
where i will have to put the definition, but i dont want the
constructor to be able to alter this definition!

In the blessed hashref style, there is no information hiding at all. Not
only can the constructor change the object (well it has to, since it
*creates* the object in the first place), but anybody who gets the
object can examine and change its contents - it is just a hashref, after
all.

hp
 
D

Dave

Hello all,

I am making the transition from .Net to Perl for a new project and i
have a few questions about OO Perl, that i would be most grateful if
someone could comment on / answer!

Many thanks for any of your help,


This problem is conceptual, in that i am waiting to learn about certain
features before trying to actually implement this - so i'll put
together a dummy OO module and a calling script.

------------------------------------------------------------------------------------------
****************
* Perl Module *
****************
package MyPackage;


#Use definitions
use strict;


#Contructor for MyPackage Class
sub new {
my ($class, %arg) = @_;
bless {
_myFirstName => $arg{myFirstName},
_myLastName => $arg{myLastName},
_myAge => $arg{myAge},
_myFavouriteArtists => $arg{myFavouriteArtists},
}, $class;
}


sub ShowAgeAndArtists{

my $self = $_[0];

#Dereference array
my @myFavouriteArtists = @{$self->{_myFavouriteArtists}};

my $artist = '';

foreach $artist ( @myFavouriteArtists )
{
print "$self->{_myFirstName} aged $self->{_myAge}, likes $artist\n";
}


}

1;
------------------------------------------------------------------------------------------


------------------------------------------------------------------------------------------
********************
* Calling script *
********************
#!/usr/bin/perl -w

use strict;
use MyPackage;

my $myFirstName = 'Ben';
my $myLastName = 'Wilder';
my $myAge = 28;
my @myFavouriteArtists = qw[Wolfmother LedZep ChesneyHawkes];

#Instantiate MyPackage class
my $myPackage =
MyPackage->new(myFirstName=>$myFirstName,myLastName=>$myLastName,myAge=>$myAge,myFavouriteArtists=>\@myFavouriteArtists);

$myPackage->ShowAgeAndArtists();

------------------------------------------------------------------------------------------

Now that i have my example, i'd like to ask a few questions:

1. I am passing a reference to the @myFavouriteArtists array, however
with objects being in my mind, an encapsulation of data, it this the
most effective way to pass array data to an object? Can i pass the data
by value so that the object deals with its own copy of the data - or am
i being silly?


2. In MS .Net there is the principle of object fields, object instance
scoped, not class scoped, that are not accessible through any public
methods, but are used possibly by methods private to the class. For
example - in the above scenario i may want to implement a boolean flag
"IsTooOld" that can be set by a private internal method for the
purposes of a logical decision elsewhere in the module. How can i
define such a variable in perl? It seems that since i am blessing the
hash into object-hood, that is where i will have to put the definition,
but i dont want the constructor to be able to alter this definition!
Any ideas (please let me know if i havent put explained this well
enough!)


Thanks again for any help!

Now go easy on the perl noob guys *backs into corner*


Ben

You should have a look at Conway's 'Perl Best Practices' that has some
strong advise about how to implement OO Perl. It goes without saying that
this advice is not universally accepted in the Perl community but it is
worth considering nontheless. [I believe Schwarz's 'Intermediate Perl' has
different OO Perl recommendations but I have not read that one].

Dave
 
A

Anno Siegel

Peter said:
(e-mail address removed) wrote:
[snippage]

So I think, passing references to arrays is generally the best way.

You can of course make a copy of the array in the constructor:

_myFavouriteArtists => [ @{ $arg{myFavouriteArtists} } ],

if you want to. I found this rarely to be necessary,

It is practically always necessary to copy any references before
using them in an object. Just consider a loop like this:

while ( <> ) {
my ( $first, $last, $age, @artists) = split;
push @somewhere, MyPackage->new( $first, $last, $age, \ @artists);
}

All the objects created in this loop will *share* their internal
myFavouriteArtists array and will show the same favourite artists
unless you copy the array on object creation.

Anno
 
P

Peter J. Holzer

Anno said:
Peter said:
(e-mail address removed) wrote:
[snippage]

So I think, passing references to arrays is generally the best way.

You can of course make a copy of the array in the constructor:

_myFavouriteArtists => [ @{ $arg{myFavouriteArtists} } ],

if you want to. I found this rarely to be necessary,

It is practically always necessary to copy any references before
using them in an object. Just consider a loop like this:

while ( <> ) {
my ( $first, $last, $age, @artists) = split;
push @somewhere, MyPackage->new( $first, $last, $age, \ @artists);
}

All the objects created in this loop will *share* their internal
myFavouriteArtists array and will show the same favourite artists
unless you copy the array on object creation.

Actually, no. In this case each run through the loop will create a new
instance of @artists.

Run this program, and you will see that the addresses will change and
that the contents are correctly preserved:

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
#!/usr/bin/perl
use warnings;
use strict;

package Foo;

sub new {
my ($class, $arr) = @_;
my $self = { arr => $arr };
bless $self => $class;
return $self;
}

sub getarr {
my ($self) = @_;
return $self->{arr};
}

package main;

my @foo;
while (<>) {
chomp;
my (@arr) = split;
push @foo, Foo->new(\@arr);
}

for (@foo) {
my $arr = $_->getarr;
print "$arr @$arr\n";
}
----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----

There are cases where copying is necessary, but since this can only be
determined from reading the code of the caller I find it more logical to
do the copying in the caller:

push @foo, Foo->new([@arr]);

$arr[5] = "foo";
push @arr, "baz";

push @foo, Foo->new([@arr]);

Or something like that.


hp
 
A

Anno Siegel

Peter said:
Anno said:
Peter said:
(e-mail address removed) wrote:
[snippage]

So I think, passing references to arrays is generally the best way.

You can of course make a copy of the array in the constructor:

_myFavouriteArtists => [ @{ $arg{myFavouriteArtists} } ],

if you want to. I found this rarely to be necessary,

It is practically always necessary to copy any references before
using them in an object. Just consider a loop like this:

while ( <> ) {
my ( $first, $last, $age, @artists) = split;
push @somewhere, MyPackage->new( $first, $last, $age, \
@artists);
}

All the objects created in this loop will *share* their internal
myFavouriteArtists array and will show the same favourite artists
unless you copy the array on object creation.

Actually, no. In this case each run through the loop will create a new
instance of @artists.

You are right, my example doesn't show what I wanted to show. If
@artists was declared outside of the loop for some reason, the effect
I described will happen (untested again):

my ($first, $last, @artists);
push @somewhere, MyPackage->new( $first, $last, $age, \ @artists)
while <>;

In general, it is not a good idea to use references given from outside
directly in an object. If you do, the users of the class must be
told.

Anno
 
B

ben.wilder

Gentlemen,


Thank you all very much for your help. Your explanations very much
appreciated. I have now the Conway OO perl book and will investigate!
The copying array in the constructor sample is invaluable and your
explanation of class / object behaviour has improved my understanding
of how Perl implements OO a great deal!

Thanks again for your time!

Ben
 
C

Charles DeRykus

Dave said:
Hello all,
...

You should have a look at Conway's 'Perl Best Practices' that has some
strong advise about how to implement OO Perl. It goes without saying that
this advice is not universally accepted in the Perl community but it is
worth considering nontheless. [I believe Schwarz's 'Intermediate Perl' has
different OO Perl recommendations but I have not read that one].

s/Schwarz/Schwartz/. I haven't read `Intermediate Perl` yet but I assumed
its OO approach and recommendations wouldn't differ greatly with PBP's.
Can anyone who's read both contrast them...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top