CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
My First OOP Programme, advice needed

 



Zhris
Enthusiast

Feb 27, 2011, 8:54 AM

Post #1 of 4 (992 views)
My First OOP Programme, advice needed Can't Post

Hello,

This is my first ever OOP programme and I would really like some feedback and advice on improving it. It has no real use, just an example I could apply my knowledge to.

Here are a few questions:

1) I'm struggling to understand the namespacing of $class and $self. If possible could someone explain when to use $class and when to use $self.
2) Is there a better way, other than to pass a subroutine condition as an arguement, to filter the return list. It works very nicely, giving me lots of freedom, but I can't remember seeing code like it before.
3) If I did e.g. "$$self[$index]{$key}" to access the objects data from inside the package, would this be acceptable. I'm not sure if the whole "looking inside the black box" applies to the method code alone, or code inside the package as well.
4) In the newPeople method, I do "bless $person;" to create an object inside an object, is this what an "instance" is.

Thank you very much in advance to anyone who is kind enough to take the time to look through my code.

Chris


Code
#OOP programme to insert, manipulate + display fields from an array of hash references containing key/value pairs. 

#//////////////////////////////////////////////////

#!/usr/bin/perl
use strict;
use warnings;
use Cwd;
use CGI ':standard';
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
print "Content-type: text/html\n\n";
warningsToBrowser(1);

#//////////////////////////////////////////////////

{
package profilePeople;

use strict;
use warnings;
use Storable;
use Data::Dumper;

sub AUTOLOAD { #input=N/A, output=N/A [default method if a method does not exist]
our $AUTOLOAD;
$AUTOLOAD =~ s/.*:://s;
die "Method $AUTOLOAD is not a method of the profilePeople package.\n";
}

sub new { #input=hash ref(s), output=object [overwrites existing object]
my $class = shift;
my @people = @_;
my $self = [];
my $return = undef;

bless $self, $class;
$self->newPeople(@people);
$return = $self;

return $return;
}

sub newFromDisk { #input=scalar then hash ref(s), output=object [overwrites existing object]
my $class = shift;
my $path = shift;
my @people = @_;
my $self = retrieve $path;
my $return = undef;

die "Data reference retreived from disk is not an object of the profilePeople package.\n" unless (eval { $self->isa('profilePeople') });
bless $self, $class;
$self->newPeople(@people);
$return = $self;

return $return;
}

sub outputToDisk { #input=scalar, output=true
my $self = shift;
my $path = shift;

store $self, $path;

return 1;
}

sub newPeople { #input=hash ref(s), output=array ref
my $self = shift;
my @people = @_;
my $return = [];

foreach my $person (@people) {
bless $person; #create an instance in the current package
push @$return, $person;
push @$self, $person;
}

return $return;
}

sub updatePeople { #input=hash ref then subroutine ref, output=array ref
my $self = shift;
my $update = shift || {};
my $condition = shift || sub{1};
my $return = [];

foreach my $index (0 .. $#$self) {
if (&$condition($self->[$index])) {
while (my ($key, $value) = (each %$update)) {
#if ($value eq '') { delete $self->[$index]{$key}; next; } #removes key/value pairs where the value is empty
$self->[$index]{$key} = $value;
}
push @$return, $self->[$index];
}
}

return $return;
}

sub deletePeople { #input=subroutine ref, output=array ref
my $self = shift;
my $condition = shift || sub{1};
my $return = [];

foreach my $index (0 .. $#$self) {
if (&$condition($self->[$index])) {
push @$return, $self->[$index];
delete $self->[$index];
}
}

return $return;
}

sub findPeople { #input=subroutine ref, output=array ref
my $self = shift;
my $condition = shift || sub{1};
my $return = [];

@$return = grep { &$condition($_) } @$self;

return $return;
}

sub getValues { #input=scalar(s), output=array ref
my $self = shift;
my @keys = @_;
my $return = [];

foreach my $key (@keys) {
push @$return, $self->{$key};
}

return $return;
}

sub dumpObject { #input=N/A, output=scalar
my $self = shift;
my $return = undef;

$return = Dumper($self);

return $return;
}
}

#//////////////////////////////////////////////////

my $object = profilePeople->new(
{'name' => 'peter'},
{'name' => 'william'},
{'name' => 'chris', 'age' => "20", 'sex' => 'male'},
{'name' => 'john', 'age' => "44", 'sex' => 'male'},
{'name' => 'laura', 'age' => "22", 'sex' => 'female'}
);
#####
my $up = $object->updatePeople(
{'name' => 'newname', 'age' => "", 'location' => 'UK'},
sub{$_[0]->{'name'} eq 'chris'}
);
#####
my $dp = $object->deletePeople(
sub{$_[0]->{'name'} =~ m/laUrA|john/i}
);
#####
my $np = $object->newPeople(
{'name' => 'emily', 'age' => "28", 'location' => 'UK'},
{'name' => 'chris', 'age' => "41", 'sex' => 'male'},
);
#####
my $fp = $object->findPeople(
sub{($_[0]->{'age'} > 21) || ($_[0]->{'name'} =~ m/W/i)}
);
#print $_->{'name'}, '<br />' foreach (@$fp); #its bad to look inside the black box
foreach my $person (@$fp) {
print join ', ', grep { defined } @{$person->getValues('name', 'age')}, "<br >\n";
}
#####
$object->outputToDisk(getcwd().'/store.txt');
my $newobject = profilePeople->newFromDisk(
getcwd().'/store.txt',
{'name' => 'lucy', 'age' => "27", 'sex' => 'female', 'location' => 'USA'}
);
print $newobject->dumpObject;
#####
#$newobject->nonexistantmethod;

#//////////////////////////////////////////////////



(This post was edited by Zhris on Feb 27, 2011, 9:07 AM)


FishMonger
Veteran / Moderator

Feb 28, 2011, 9:07 AM

Post #2 of 4 (927 views)
Re: [Zhris] My First OOP Programme, advice needed [In reply to] Can't Post

I have done very little OO programming myself, but will try to answer your questions.

1) $class is the package, in this case it's profilePeople, and $self is the object that holds the data. The only time I use $class is when I'm creating the object i.e., inside the 'new' method.

2) I'm not 100% sure I understand your question. Write an accessor method to filter/return a person and another accessor method to return multiple people.

3) I would write that as $self[$index]->{$key} and would be inside the method. The last part of this question implies you want to use that outside of a method i.e., as a global package variable. The answer to that would be no.

4) An object is known as a '"class instance" and an object method is known as an "instance method". Does that answer your question?

Creating an object for every person as you are doing in the newPeople method is the wrong approach. Lets look at this from a different angle. Lets say that profilePeople is a mysql database and the newPeople sub inserts people into the database. You would not want to create a completely new duplicate table for each person you add. You would simply add them to the existing table.

So, in your case instead of using bless, you'd use push to add a new element/record. Your object should be a ref to an AoH.

On a side note, it would be better to extract the package code and put it in its own .pm file instead of including it inside your main script.


FishMonger
Veteran / Moderator

Feb 28, 2011, 9:12 AM

Post #3 of 4 (926 views)
Re: [FishMonger] My First OOP Programme, advice needed [In reply to] Can't Post

I forgot one important recommendation.

The best OO book I've found is "Object Oriented Perl" by Damian Conway. It's an old book (1999) but everything still holds true.


http://www.manning.com/conway/


Zhris
Enthusiast

Feb 28, 2011, 3:39 PM

Post #4 of 4 (902 views)
Re: [FishMonger] My First OOP Programme, advice needed [In reply to] Can't Post

Dear Fishmonger,

Thank you very very much for getting back to me. You have cleared a few things up.

Firstly, thank you for providing that book title. I currently use O'Reilly Intermediate Perl, which seems to breeze through OOP. I took a look at "Object Oriented Perl" on Amazon, and it seems to be what i'm looking for.

1) Cleared that up. That makes perfect sense.

2) I'm not 100% sure I understand what answer I was looking for. I'll have to put more thought into using multiple accessor methods which you suggested.

3) Since asking that question I was able to derive that I can do whatever I want with the object inside the package, but I must never access it directly outside the package. At first I didn't comprehend that an object is simply a reference.

4) That kind of answers my question. Again ill have to put more thought into it. As this is all new to me I need to take the time to read up on and define key terms which I haven't heard of before (class instance, instance method), just for further clarrification.

Ok I totally see where you are coming from, that an object within an object is unneccessary. I don't yet understand the alternative, as when it comes to accessing individual users, i.e.

Code
foreach my $person (@$fp) {  
print join ', ', grep { defined } @{$person->getValues('name', 'age')}, "<br >\n";
}

$person wouldn't be an object, therefore it would throw an error? Ill have to have a play around with my code to see if I can get it to work in a way you described.

Finally, I kept everything in 1 script just for ease whilst playing with the code. In future I will separate package from code.

Thanks once again, your knowledge is extremely valuable to me, and I found your response very helpful.

Chris

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives