Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

OO Getters/Setters

by theAcolyte (Pilgrim)
on Dec 31, 2003 at 14:11 UTC ( [id://317885]=perlquestion: print w/replies, xml ) Need Help??

theAcolyte has asked for the wisdom of the Perl Monks concerning the following question:

I've been reading up on Perl/OO lately and there's one thing (okay, there's a lot of things, but this one mostly, right now) that is bothering the heck outta me: getters and setters for object data.

I understand the idea of not having the object's data acessed directly (like $self->{'myDataField'}) because:

  • It violates encapsulation
  • If you change the data struction of the item later, people's programs will break

So, the solution to this is accessing data or setting it via methods, right? I'm clear on that too. Now, I've seen a getter/setter in OO tutorials, one written for each attribute. Then I've seen the module class::accessor which auto-generates them for you, and then calls a generic get/set for each method (so you can change the underlying data structure by overrideing get/set in your subclass).

Now here is where I'm confused: Why do you need a different method for each bit of data? The first thought in my mind after reading the class::accessor documention was why not do something like this:

$myObject->getValue("myDataField"); $myObject->setValue("myDataField","New Data");

or even ...

$myObject->dataValue("myDataField","")

Where filling in the second value makes it a setter, a blank or non-existant 2nd field would be a getter.

This type of set up should solve the problems of encapsulation, and of being able to change the way data is stored later, even via subclassing if a user desires. But something tells me that I must be missing something ... or else things like class::accessors wouldn't exist.

So, here's the query: Is there a reason I shouldn't do it the way I've just outlined above? If there is, I'd love to know it. Pehaps I've misunderstood encapsulation, or OO in general. I'm -very- new to OO-style programing.

I hope someone here can enlighten me :-)

# theAcolyte

Replies are listed 'Best First'.
•Re: OO Getters/Setters
by merlyn (Sage) on Dec 31, 2003 at 16:46 UTC
    An object's interface to the "outside" is its methods. The methods should be thought out well, because refactoring and upward compatibility demand that those methods retain the same or better functionality over time when installing "plug-replacable" parts.

    There are times when a method's natural implementation is to take a parameter and alter an instance variable directly, or to fetch an instance variable and return it with minimal fuss. For these, an automatically-constructed getter or setter is quite expedient.

    But the blanket "give every instance variable a getter/setter" strategy is nearly always a sign that the coder is not yet "thinking objects", as you point out. Methods should be named by the "what" it does, and not "how" it gets done.

    For example, consider a two-space point type, with a natural X and Y value for the addressing. I might initially create the traditional getter/setter for these two independent values. But let's say later that I discover that more often than not, I need these values as rho and theta in polar space, so I decide to change the internal representation to polar space. With separate X and Y setters, I have to perform more needless calculations as I update each one, where if I had just given an interface initially that only set both at once, my calculation burden would have been reduced.

    Yes, there are holes in that example... that's just a quick thought. The point is that providing attribute-based setters instead of functional interfaces means you are bound to support an interface that may be expensive later when you change the design. I hope that's clear.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      Ok -- this makes more sense to me, but I'm going to restate in my own words, and if you have a second to let me know if I'm understanding, I'd appreciate it.

      Generic get/set methods can be bad - especially with anything more complicated then a piece of information that's unlikely to change.

      There's nothing inherently wrong with

      $object->get("theIDData")

      Except it isn't entirely object oriented in terms of thought process (what vs how), and that it limits you if you decide to make changes later. Example: if an object instance has an "id" attribute, you may someday want to take that id from, say, a database, and change it before you hand it back to the calling program, because there's a new ID structure in place, but the old database can't (for some mythical reason) be changed.

      So .. a simple ->get ->set set of methods can be appropriate, but only if you never expect to have to do any work with the data placed inside the object.

      I'm slowly getting my mind wrapped around the basics of OO with the help of this site, and all its tutorials. Thanks again for everyone's input.

      # Erik

        I will offer this final piece of advise. Encapsulation is always your friend. Many would disagree, but my experience affirms this truth. Whether or not it is trivial encapsulation, it will save you from unforeseen headaches %99.9999 of the time. If you use the encapsulation that you've built within your object instead of direct member access, you can take advantage of the encapsulation within as without. Remember the goal of OOP is to bundle data with behavior, so that the rest of your code doesn't have to know the details. OOP is not just some convenient function access technique. Perl does lots of things well, but I think that it helps teach and propagate bad OOP habits. Some have argued here that creating trivial accessors/mutators is no different than making them public. Logically that is true, however, the difference will be seen on the maintenance side. If you start setting and getting values via the member variables (a.k.a attributes) now, sure they may be static, but now you have code spattered around that are using the members directly, and when the time comes that you want to update a persistence store, or calculate some value before presentation or persistence, you have a lot more code to change when you compare it with no code changes. I guess if it's a 5 line script that's not too bad, but if so why use objects? Remember objects aren't data structures, they're state machines. Would you want to cross wires just to change the channel on your tv? No, you want to use the remote. You want those attributes of managing the tv to be abstracted away from you. Then when you get a new tv, sure there might be more buttons, but the interface is the same.

        And in the "For What It's worth" department, the
        $obj->get("SomeKey");
        $obj->set("SomeKey", "some val");
        Notation makes me cringe, IMHO no encapsulation is better than that.

Re: OO Getters/Setters
by dragonchild (Archbishop) on Dec 31, 2003 at 14:58 UTC
    Personally, if I have to uses accessors/mutators, I use the following:
    # Assume I have foo, bar, and baz as my attributes my $x = $self->foo; # This retrieves the value in foo $self->foo(123); # This sets foo to 123 (and returns $self, to all +ow for: $self->foo(123) # chaining of mutator calls ->bar(456);

    Now, I have used generic set/get routines. We had a set() and a get(), that took attribute names. So, you would have:

    my %values; @values{qw(foo bar baz)} = $self->get(qw(foo bar baz)); $self->set( foo => 123, bar => 456, );

    Except, there are a few issues. How do you handle misnamed attributes? What about attributes whose values are arrayrefs? It gets messy. And, it's even worse if you have a single method for doing all accessor/mutator functionality. Personally, I'd recommend against this. Maintainability will suffer.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      $self->foo(123) # chaining of mutator calls ->bar(456);
      this always gave me the heebies because returning the object for success seems a little fragile. What if ->foo() fails? Is it still sensible to return the object? What happens if you have a mutator (well, a not-mutator according to hardburn) that rejects the data presented for whatever reason? If your mutator returns an error code because ->foo(123) is bad input, you wind up with, i.e., "123 is an invalid foo"->bar(456) and your code goes pear-shaped.

      Sure, maybe "123 is an invalid foo" is up for argument as a sensible error code. The point is that it seems like a way to limit your object if you need to do any interesting things to communicate failure to back to your user.

        this always gave me the heebies because returning the object for success seems a little fragile.

        I couldn't agree more, and not only is returning the object fragile, but the whole idea of an accessor and a mutator (getter and setter) rolled into one subroutine seemed to be to be just a opening for interface confusion. What if i want to provide an accessor, but not a mutator. Now convention is broken, and programmer assumptions are thrown out the window. While 'getField' and 'setField' are tedious to code initially, they have a tendency to pay off in terms of maintainability and ease of understanding for others.

        What if ->foo() fails? Is it still sensible to return the object? What happens if you have a mutator (well, anot-mutator according to hardburn) that rejects the data presented for whatever reason? If your mutator returns an error code because ->foo(123) is bad input, you wind up with, i.e., "123 is an invalid foo"->bar(456) and your code goes pear-shaped.

        I would argue here though that if '->foo()' fails, then you should throw an exception, and not return an error code. In which case the method chaining doesn't matter, since you would've longjump-ed outta there already.

        -stvn
Re: OO Getters/Setters
by !1 (Hermit) on Dec 31, 2003 at 15:12 UTC

    First off sorry if this seems like a rant, but I've been up all night.

    This is a lesson most programmers who are new to OO should learn right up front: there are objects and then there are data structures. Objects store state. Data structures store data. Why is this important? Because you sometimes end up with a really goofy interface to objects when someone decides, "hey, let's make everything in our blessed hash accessible through getter and setter methods." Interaction with an object should affect its state. Granted there will be methods to alter the data that the object is working upon. However, these methods will usually do a few things such as check current state, alter other values, and possibly even reject certain changes it deems as invalid. If you are going to just use something that automatically generates these getters and setters, why even bother? It's a blessed data structure. Let them just use the access methods provided by the data structure. Some may argue with me on this point because there's oogity boogity stuff about allowing someone to reach into the object and change it directly. But aren't you already doing that by offering these getter/setting methods? Certainly at some point in the future you may decide to change the underlying structure and decide to still provide the old methods for backwards compatibility. My response to this is that you ought to plan out your object. Your interface shouldn't reflect the underlying object data structure. Your underlying data structure, however, should support the object.

Re: OO Getters/Setters
by hardburn (Abbot) on Dec 31, 2003 at 14:49 UTC

    So, the solution to this is accessing data or setting it via methods . . . I've seen a getter/setter in OO tutorials, one written for each attribute.

    Yes, most tutorials do it that way. They're wrong, or at least misleading. Getter/setter methods (or for those who like fancier words, accessors/mutators) should be avoided. Sometimes you do need them on a few attributes, but if your design calls for accessors/mutators on every internal attribute, you need to rethink your design. Doing it that way won't really result in an object, but a datastructure that happens to be accessed with an object-like syntax. The only difference between this:

    $obj->{field};

    And this:

    $obj->field();

    Is some fancy syntax. I bet the second form is slower, too (haven't benchmarked it, though).

    Now when you actually do need accessors/mutators . . .

    Why do you need a different method for each bit of data?

    True to TIMTOWTDI, Perl offers many ways of producing accessors/mutators. Your way isn't necessarily wrong. If use strict 'subs'; was useful for method lookups, then I think you can make a stronger argument against what you're doing (since a method-per-attribute way would give you compile-time errors when you make a typo). Since methods are all looked up at run time, use strict 'subs'; isn't particularly useful no matter how you do it.

    One problem with your way of generating accessors/mutators is that, unless you do checking inside the method, a user can insert an attribute that didn't previously exist.

    There are other ways of generating accessors/mutators. One is to use AUTOLOAD, but it's slow. Class::Accessors works by using closures, like this:

    my @FIELDS = qw( a b c d ); # Put your field names here foreach my $field (@FIELDS) { no strict 'refs'; *$field = sub { my $self = shift; $self->{$field} = shift if @_; return $self->{$field}; }; }

    The symbol table will make a reference to the same subroutine each time, thus saving memory. It's also as fast as any other method lookup. If you wrap the above in a BEGIN block, there will be no runtime hit for generating the methods.

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

      I really have to disagree with your first supposition. There is a world of difference between $obj->{field} and $obj->field(). In fact, the difference is one of the cornerstones of oo.
      With the direct access, I have no control over the field. The consumer can store anything it it, squirrel away references to it, change the internal state of my object from a distance using those references, etc.
      With the accessor, I have complete control over the field. I can validate changes to the field using any criteria I choose. I can change storage methods. I can manipulate the data internally. I can do anything I need to, without breaking the users code and without the user breaking my code.

      UPDATE: It also removes an entire level of error prevention. There is no way to detect mistyped fieldnames. The following code fails silently bacause it is syntactically correct, and without accessors there is no mechanism to detect bad field names.
      my $var = $obj->{field}; $var = someManipulation($var); $obj->{feild}=$var;


      -pete
      "Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."

        With the direct access, I have no control over the field.

        In Perl you do, via tied scalars in the internal attributes. I don't think this method is common, but it can be done.

        With the accessor, I have complete control over the field.

        If you're doing something more complex than setting or getting an attribute (no validation, etc.), then it's not a true accessor or mutator.

        ----
        I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
        -- Schemer

        : () { :|:& };:

        Note: All code is untested, unless otherwise stated

        While this is true (and valid), there is something to be said about one of the worst abuses of OO, particularly among Java programmers....writing a getter/setter for every method to avoid making variables public. This is misguided.
        private int foo; public int getFoo() { return foo; } public void setFoo(int foo) { this.foo=foo; }

        In this case, the programmer thought that they were preventing access to an internal variable that should have been encapsulated, but they wrote methods that completely bypass that encapsulation. In this case (with no logic restricting input values), they have essentially made the "int foo" public, which is what they were avoiding in the first case. Also, this introduces greater overhead in calling additional functions.

        I would much rather see OO folks think in terms of objects and have their methods have meanings, rather than to see them implement code as merely data structures with optional bounds checking behavior.

        Essentally (and I am speaking in that accursed Java language):

        Here the methods have meaning to the object thing.move(+1,-1); Here we just have a "objectified" data structure and we aren't really OO thing.setX(thing.getX()+1,thing.getY()-1)

        So if you want to be all "Object Oriented", that's fine, but most most programs that declare them to be OO simply are not, they are just restricted data structures that are often 5x more complicated than they need to be. "Functions operating on Data" worked fine for many years and there is nothing wrong with it. For many programs, it's healthy to have a few objects mingling with non-OO constructs, since pure OO can really be detrimental in many cases. It can steamroll out of control (especially if you write a function for most variables!)

        In fact, developing good data structures and programs are really a lost art, as many OO zealots can develop huge OO tree hierachies that obfuscate a lack of good up-front design. Sooner or later, a simple program that could have been written in a 1000 lines is now written in 1000 files!

Re: OO Getters/Setters
by exussum0 (Vicar) on Dec 31, 2003 at 15:35 UTC
    Now here is where I'm confused: Why do you need a different method for each bit of data? The first thought in my mind after reading the class::accessor documention was why not do something like this:
    $myObject->getValue("myDataField"); $myObject->setValue("myDataField","New Data");
    Frankly you don't need to and your second set value is perfectly valid. For instance, if you are working with a custom Date object, having a setMonth, setYear and setDateOfMonth would be invaluable, but for convenience, it be great to have something like, setDate($year,$month,$day). The less typing you do, the less chance of a bug, right?

    Also, if you want to make sure that two values which are tightly related and have some sorta dependency on each other, it's less-end-developer-error-prone to put the two together. For instance, if you have an interface for configuring network cards, having a setIp and setHostmask would cause less headache, since assuming a hostmask may be bad (in your eyes).

    Finally, sometimes it's a requirement that they both get done. Having something like a prepared sql statement and setting the placeholders, it'd be bad design to do $sth->bind_param($p_num) and then $sth->bind_param($bind_value). Allowing one to get done w/o the other (if they existed) would be an error. $sth->bind_param($p_num, $bind_value) is an example of one that makes perfect sense to do as a dual parameter set. And binding the same parameter twice would be a strange thing to do.

    The only time you shouldn't do a mass-set $object->setALotOf Values is either...
    .. for convenience, where a pattern of sets occurs over and over again
    .. for preventing certain patterns, since though they can be done, it'd just be silly
    .. for preventing one thing from happening w/o the other since one acts upon the other and can't be seperated


    Play that funky music white boy..

      . . . for convenience, it be great to have something like, setDate($year,$month,$day)

      Better still is to have a setDate method take a DateTime object, or one of CPAN's multitude of other Date objects, if you really want to.

      ----
      I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
      -- Schemer

      : () { :|:& };:

      Note: All code is untested, unless otherwise stated

        Of course, but the point is of theoretical example with something that everyone can relate to that is tightly coupled. :)

        Play that funky music white boy..
Re: OO Getters/Setters
by TomDLux (Vicar) on Dec 31, 2003 at 19:11 UTC

    When you have an object, you need to send the object messages, that is, to activate the object's methods, rather than to access the object's attributes.

    If you do have accessor methods, they are intended to control access by the class's methods, friends and subclasses, rather than for use in ordinary methods using the class. That is, the attributes are private, the accessors are protected

    For example, a bank account class would need ( at least ) two attributed: balance and id number. The id number is set when the account is created and never changed after that. How the id number is read doesn't matter much, whether with an accessor or as a variable ... except that accessing the variable contains the risk of someone changing the number.

    On the other hand, how you set the balance is more critical. In fact, you should never set the balance, you should deposit(amount) and withdraw(amount).

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://317885]
Approved by hardburn
Front-paged by coreolyn
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (7)
As of 2024-04-24 09:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found