Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Object Oriented Perl - very basic guide

by Preceptor (Deacon)
on May 15, 2014 at 14:10 UTC ( [id://1086139]=perlmeditation: print w/replies, xml ) Need Help??

I've been doing Perl for a while now, and one of the things I've meant to get to grips with, is 'object oriented' Perl. I think I'm getting there now, so I'm writing up what I've got so far in the hopes that it'll be a helpful reference.

First, the basics - object oriented languages encapsulate code into 'objects'. The object has an internal state, and it's own 'code' for handling that state.

The reason that's useful, is because if I write a module for you to use - then all you need to know is how to run the internal code, and don't have to worry about what goes on inside.

That's why it's widely used in modules on CPAN, and chances are pretty good that you've already used it if you've used any CPAN modules.

The way perl 'does' objects, is that it treats each object as a hash. The 'attributes' or 'internal state' are hash keys.

As a start point (save as 'MyObject.pm')

#!/usr/bin/perl use strict; use warnings; package MyObject; sub new { print "method 'new' called with parameters: ", join ("\n", @_ ), "\n +"; my ( $class, @args ) = @_; my $self = {}; $self -> {state} = "newly created"; bless $self, $class; return $self; } sub set_state { my ( $self, $new_state ) = @_; $self -> {state} = $new_state; } sub get_state { my ( $self ) = @_; return $self -> {state}; }

You can then 'drive' this module with:

# use_oo.pl #!/usr/bin/perl use strict; use warnings; use MyObject my $object_instance = MyObject -> new(); print "Object is: ", $object_instance,"\n"; $object_instance -> set_state ( "here is a new state" ); print "Object state: ", $object_instance -> get_state(),"\n";

It's fairly simple, but what it means is that I can 'build in' all sorts of code and state variables into 'MyObject' - for example, validating when you 'set state' to ensure that it's not forcing something odd to happen. But this can happen transparently, and your code snippet doesn't actually need to worry about it.

What you may not have seen before is the 'bless' keyword - when you create '$self' then you create a hash reference - which you could (and essentially, do) treat it as any other hash reference. What 'bless' does is 'mark' the reference as a specific type of object. So you'll see when you print '$object_instance' that it looks something like:

MyObject=HASH(x012345)

That's key, because it lets perl identify where it should look when you use a method - e.g. 'get_state' - it knows to look inside the 'MyObject' module explicitly.

$object_instance -> set_state ( "here is a new state" );

is translated by perl (because it knows it's a 'MyObject' because of that 'bless') into:

MyObject::set_state ( $object_instance, "here is a new state" );

(Passing $object_instance is necessary, because that has it's own internal state, and we need to know which internal state to modify).

I wouldn't say it's something I'll always use as a technique, but it's a useful extra tool - if I've got a set of scripts that all do similar and related things, then there may well be value in creating a module for them to all use... and if you do, it's certainly worth considering using an object oriented module instead.

Replies are listed 'Best First'.
Re: Object Oriented Perl - very basic guide
by choroba (Cardinal) on May 15, 2014 at 15:00 UTC
    The way perl 'does' objects, is that it treats each object as a hash.
    That's not the full story. In Perl, any reference can be blessed. Hashes are the easiest thing to make into an object, but people sometimes bless array references, too (they are a bit faster in tight loops). You can even bless a scalar (check XML::LibXML) or code reference...
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      You can even bless a scalar
      Just for clarification (you meant that of course): You cannot bless a scalar, you can bless a reference to a scalar (as any other type of reference - I would recommend to bless a directory handle :-)
        You didn't quote the whole phrase: You can ever bless a scalar (...) or code reference.
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Object Oriented Perl - very basic guide
by Arunbear (Prior) on May 15, 2014 at 15:57 UTC
    The reason that's useful, is because if I write a module for you to use - then all you need to know is how to run the internal code, and don't have to worry about what goes on inside.
    The same can be said of any module whether it be object oriented or not e.g File::Find.

    Some reasons that objects are useful:

    Namespacing

    Create an object and you have access to all it's functionality, no need to import various functions as you would with procedural code.

    Polymorphism and Encapsulation

    This is the ability to hide and swap out different implementations, exemplified by DBI (code copied from it's manual):
    $sth = $dbh->prepare(q{ SELECT region, sales FROM sales_by_region }); $sth->execute; my ($region, $sales); # Bind Perl variables to columns: $rv = $sth->bind_columns(\$region, \$sales); # you can also use Perl's \(...) syntax (see perlref docs): # $sth->bind_columns(\($region, $sales)); # Column binding is the most efficient way to fetch data while ($sth->fetch) { print "$region: $sales\n"; }
    You can write code like that whether your database is MySQL, PostgreSQL or any other supported by DBI. Each DBD::* driver module has methods implementing the familiar DBI interface whilst also encapsulating the details of how to communicate with a specific database.
Re: Object Oriented Perl - very basic guide
by InfiniteSilence (Curate) on May 15, 2014 at 18:29 UTC

    There are some things wrong here and some other things that can be improved:

    • "...object oriented languages encapsulate code into 'objects'.." When discussing object oriented languages or OO extensions to existing languages there are four concepts that need to be discussed conceptually - encapsulation, (data) abstraction, polymorphism and inheritance (see http://codebetter.com/raymondlewallen/2005/07/19/4-major-principles-of-object-oriented-programming/ for a long treatise on each). Perl offers interesting approaches to these concepts -- some of which are seen as OO heresy. Your code example shows data abstraction (for the state) without being clear that it really does not control access for example. It is easy to say $object_instance->{state} = 'bogus state';. A common assumption is that an object can hide its data from the outside world and force a user to manipulate it only in controlled ways. In Perl 5 you can violate that.
    • An example that manually sets state makes little sense. If you had chosen a better example this might not have been an issue because it would have been obvious that it is probably a bad idea to create a setter for the internal state of your object! If we were talking about a washing machine object then it might accept commands to start/stop/etc. from the user and internally set its own state. Only a getter is needed for state and, preferably, should instead be in the form of named methods for important states, like, $object_instance->isCreated() for clarity.
    • Your code example accepts @args in the constructor and then does nothing with them.
    • Referencing the package is somewhat oversimplified. A popular error is this: Can't locate MyObject.pm in @INC (@INC contains:. When endeavoring to prepare people for OO use in Perl 5 it is helpful to mention that Perl needs to actually know where it might find MyObject.pm. It is really not suitable to leave people with the belief that they should be in the same directory as the script.
    • Packages do not contain shebang lines.You don't run a package like you would a script and therefore that should not be in there.
    • How do you know your code will compile? When developing code and putting it directly into packages it is easy to screw things up and not notice. You should do two things to avoid this: perl -c MyObject.pm to check the module code for errors and/or write tests for every method (run h2xs -AXn MyObject and look in the /t directory for examples).

    Celebrate Intellectual Diversity

      Packages do not contain shebang lines.You don't run a package like you would a script and therefore that should not be in there.

      Sure they do :) a shebang line in a package hurts nothing

Re: Object Oriented Perl - very basic guide
by wjw (Priest) on May 15, 2014 at 14:38 UTC
    I appreciate this. I too am just(finally) diving into the OO stuff. Have been going right for Moo and Dancer and Moose, but am also faced with a contract job which is heavily OO and does not use any of the new frameworks. Needless to say, it is stretching my troubleshooting abilities beyond tolerable limits.(I am very fortunate to have a very understanding employer who is willing to let me learn!)

    That is why I find a posting like this helpful. If nothing else, it provides yet another resource for learning. Thanks for this!

    I do want to clarify one thing: you say The reason that's useful, is because if I write a module for you to use - then all you need to know is how to run the internal code, and don't have to worry about what goes on inside. That confused me a bit. I assume that you mean that I only need to know what my internal code is doing, as compared to the internal code of the module you provided. As in, I only need to know what you provide as methods/accessors. Is that correct?

    Thanks again!

    ...the majority is always wrong, and always the last to know about it...
    Insanity: Doing the same thing over and over again and expecting different results...
        Very true, and good point...and yes I use these also! :-)

        There is to me value in this meditation because it is not just about the "what to learn", but also about the "how it get learned".

        I get a lot out of that...

        Thanks for the handy list of URL's!

        ...the majority is always wrong, and always the last to know about it...
        Insanity: Doing the same thing over and over again and expecting different results...
Re: Object Oriented Perl - very basic guide
by jeffa (Bishop) on May 15, 2014 at 14:24 UTC

    You should consider using Moose instead:

    package MyObject; use Moose; use MooseX::FollowPBP; has state => ( is => 'rw', isa => 'Any' ); 1;

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    

      I'd generally agree - what I was aiming to do was outline the basics. Personally I find I understand things lots better if I do it the 'hard way' first, then throw it away and go and find a module to do it.

        I learned OO long before i learned Perl, via Java and C++. When introduced to Perl's OO, i was initially baffled. Perl has a less than perfect OO system. I would only discuss Perl's OO "internals" (i.e. having to bless a thingy to a package via a sub called "new") in an advanced chapter, after the more useful stuff like composition and roles have been discussed, personally. It tends to turn people off, seeing that new sub ...

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        

        I have held the opinion for quite a while now that better one understands "classic" Perl OO the more one can understand just what Moose (and the rest) are doing for you. And depending upon circumstances and/or goals (such as the $work environment) it might not be feasible to take advantage of them. If that is the case, then a solid understanding of classic OO allows you to do pretty much the same thing but in the restricted environment.

        My favorite tutorial by far has been the Animal Farm (Talk with the Animals) one, it is listed above but is no longer part of the core distro. :(

        It helps to remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
      Only if you need the meta object protocol. If you don't need it, Moo can do most of what Moose can but without the slow start up time and memory/dependency bloat.
Re: Object Oriented Perl - very basic guide
by sundialsvc4 (Abbot) on May 15, 2014 at 20:15 UTC

    Y’know, I think that this is a fantastic thing that you’re doing, and I hope that you’ll write more like it.   Part of the real problem with a subject about which much could be said ... is that we tend to say it all.   But when you say, in your own words and terms, what you recently needed to know and what you found out to be true ... that’s the best kind of tutorial.   More details can and will be added.

    I came to Perl from much-more explicitly object-oriented languages, and from the experience of having to write assembler-level code that interfaced with the “guts” of several of them.   (All of these pretty-much resolved the object issues at compile-time.)   When I encountered Perl and bless and all-of-that, I wondered, “where’s the rest of it?”   When the lights started coming on, I meant to write those thoughts down at that time, but never did.   I couldn’t write them the same way now.   Thanks for your excellent contribution.

      Well said...

      ...the majority is always wrong, and always the last to know about it...
      Insanity: Doing the same thing over and over again and expecting different results...
Re: Object Oriented Perl - very basic guide
by morgon (Priest) on May 16, 2014 at 01:06 UTC
    $object_instance -> set_state ( "here is a new state" );
    is translated by perl (because it knows it's a 'MyObject' because of that 'bless') into:
    MyObject::set_state ( $object_instance, "here is a new state" );
    This is actually not quite true. "set_state" could be implemented in a base-class and in the general case that base-class might not be known at compile-time, so Perl cannot do any "translations" but actually has to perform a search for the proper method in the parent-class hierarchie at run-time.

    At least that is my understanding...

Re: Object Oriented Perl - very basic guide
by GrandFather (Saint) on May 16, 2014 at 01:12 UTC
Re: Object Oriented Perl - very basic guide
by bcarroll (Pilgrim) on Jun 30, 2014 at 04:23 UTC
    A nifty little trick I like to use is to pull @_ directly into the "new" object. This works nicely when @_ contains a hashref.

    Example:

    #!/usr/bin/perl use warnings; use strict; my $object = MyObject->new( name => 'MyNewObject', debug => 1 ); print "MyObject Name: $object->{name}\n"; print "Debug setting: $object->{debug}\n"; package MyObject; sub new { print "method 'new' called with parameters: ", join ("\n", @_ ), "\n +"; my $class = shift my $self = {@_}; $self -> {state} = "newly created"; bless $self, $class; return $self; }
    Doing it this way allows you to assign attributes (variables) to the new object when it is created/instantiated.

    Another cool feature of perl objects is that you don't need to explicitly define "getters" and "setters", since all they really do is get or set an attribute value. You can accomplish the same thing by getting or setting the attribute value directly (like you would with any other hashref)

    $object->{attribute} = 'value'; #setter print "$object->{attribute}\n"; #getter
      Some hackers don't see it as an advantage. It doesn't catch typos in the constructor attributes:
      my $obj = 'MyObject'->new( name => 'Typoed', debyg => 1 );

      Also, the main point of getters and setters is encapsulation. Directly accessing the attributes breaks it. If you later decide to change the implementation, you need to change the code that touches the attributes as well.

      لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

        I've also had the discussion about whether you should have separate 'set' and 'get' methods, or whether having a single one is 'ok'. E.g.

        my $status_code = $object -> status; $object -> status ( "new_status" );

        I'm of the opinion that separating them out is good - static attributes you might think you can update in this way, because the paradigm of method calls is the same - as the method call you expect already exists, then it's only at run time you'll find that it doesn't allow changing, where if 'set_state' isn't present it's a bit more obvious.

      I'm afraid I wouldn't call that a feature - more like a bug. The whole reason to use OO in the first place is to allow encapsulation - such that you publish methods and handle what those methods actually do internally.

      That's what allows me to write a module, you to use it, and then me to bug fix, tweak, extend without breaking your code.

      It's something that does no harm at first, but promotes bad programming practice and burns you later - something perl is notorious for.

      I've seen a suggestion that basically involves trying to enforce this, via building your object around an anonymous hashref. Because messing with attributes directly breaks first time someone does it, they don't do it a second time.

      I feel largely the same about pulling in vague stuff via @_ or building an object off a hashref. It's not that it doesn't work (and in many ways, this is the biggest fault of perl - that things that _shouldn't_ work, usually do). But that you can build in some really horrific bugs and gotchas by doing it.

        I absolutely agree with everyone else here. I wouldn't recommend leveraging the features/bugs that allow you to do something that technically isn't the correct Object Oriented approach for a large project, or something that you are planning to release into the wild (CPAN).

        And yes, just because you can do it that way (as with a lot of things in perl), doesn't mean you should do it that way.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://1086139]
Approved by igelkott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (4)
As of 2024-03-29 14:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found