Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re^2: OOP's setter/getter method - is there a way to avoid them?

by tiny_monk (Sexton)
on Oct 27, 2015 at 11:56 UTC ( [id://1146097]=note: print w/replies, xml ) Need Help??


in reply to Re: OOP's setter/getter method - is there a way to avoid them?
in thread OOP's setter/getter method - is there a way to avoid them?

Hello, Laurent_R. You are right. The author of the book did not explicitly write about the inherent dangers of using accessors. Nor did I claim that he did. I was merely speaking from my viewpoint that ensued after reading chapter 9. It was merely my own abstraction. It was more personal than universal. I apologize for not being able to communicate clearly. I am new to OOP. The moment I saw $self->{'attribute'} = shift; being used in the setter and $self->{'attribute'} in the getter functions, it gave me the notion that the encapsulation was being broken. But I may be wrong. My understanding is that the object's innards should stay inside the object. That's my understanding of what Randal wrote in his book. Then again I may be wrong. I was hoping to find clarity as to whether it is perfectly legal to access the object's data directly inside the setter/getter function. While I understand that it is not advised that an API caller use $object->{'attribute'} to access or update the object's attribute outside a class, I don't understand why it seems perfectly alright for the setter and getter methods to use $self->{'attribute'} for changing and accessing attributes internally? Why? This is what Randal did not explain in this particular chapter. Does the concept of encapsulation apply to the code found in the setter and getter functions? If it is so, isn't $self->{'attribute'} = shift; a violation of the encapsulation? Or is this a privilege that setter and getter functions have over API callers?

Replies are listed 'Best First'.
Re^3: OOP's setter/getter methods - is there a way to avoid them?
by Athanasius (Archbishop) on Oct 27, 2015 at 12:56 UTC

    Hello tiny_monk,

    The moment I saw $self->{'key'} = shift; being used in the setter and $self->{'key'} in the getter functions, it gave me the notion that the encapsulation was being broken. But I may be wrong. My understanding is that the object's innards should stay inside the object.

    You are absolutely right, an object’s implementation (“innards”) should stay private to the object, to maintain encapsulation. But getter and setter methods are internal to the object — or, rather, they are internal to the class from which the object is instantiated. So it is quite appropriate for them to access the object’s innards. Encapsulation is broken only when an object’s implementation details are exposed or accessed outside the object.

    While I understand that it is not advised that an API caller use $self->{'key} to access or update the object's attribute outside a class, ...

    In this context, an object’s “API” is equivalent to its “interface”, that is, the totality of its public methods. And since the object itself is a blessed reference, it is often possible for code with access to the object to access its innards directly. But that breaks encapsulation, so external code should access an object only through its public methods.

    In your original example, the methods set_color and get_color do not break encapsulation, because they are defined within package Fruit. But suppose we add the following lines:

    $obj->{color} = 'blue'; print Dumper($obj);

    The output is now:

    22:39 >perl 1424_SoPW.pl $VAR1 = bless( { 'name' => 'apple', 'color' => 'green' }, 'Fruit' ); $VAR1 = bless( { 'name' => 'apple', 'color' => 'blue' }, 'Fruit' ); 22:40 >

    which shows that we have again altered the apple’s colour. But this time we have done so by accessing the object’s internals directly, which does break encapsulation.

    Unlike other OO languages, Perl allows this to happen, but that doesn’t make it a good idea! Here’s a famous quote from Larry Wall:

    Perl doesn’t have an infatuation with enforced privacy. It would prefer that you stayed out of its living room because you weren’t invited, not because it has a shotgun.

    But there are also techniques for enforcing privacy. See the section “Using Closures for Private Objects” in Chapter 12 of the Camel Book (4th Edition, 2012, pp. 446–9), and modules such as MooX::ProtectedAttributes for use with Perl object systems such as Moose and Moo.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      This is the answer that I have been looking for. Thank you very much, Athanasius. I sincerely appreciate the time and energy you have put into answering my question. Also, I'd like to thank you for recommending a reference book for additional information. Again, I appreciate this additional gesture. I was so bogged down by details that I could no longer see the bigger picture. I have forgotten that the setter and getter functions reside inside the object. So, even if they directly access the object's attribute, the activity is still taking place inside the object. Thank you, again Athanasius. :)

Re^3: OOP's setter/getter method - is there a way to avoid them?
by AnomalousMonk (Archbishop) on Oct 27, 2015 at 13:26 UTC

    Further to Athanasius's comments above:

    My understanding is that the object's innards should stay inside the object.

    But they can't stay inside forever. After all, an object's innards are data, and data must be accessed and sometimes even (gasp) changed in order to be useful. Consider the  'name' attribute in the example code of the OP. It has no getter/setter method. Except by direct reading or writing via the object reference, which we all agree is a Really Bad Idea and which no one will ever do, this data cannot be accessed in any way. What good is it? Of course, you can imagine you might write another method that would access it, say along the lines of

    sub string_of { my $self = shift; return "$self->{color} $self->{name}"; } sub print { my $self = shift; printf "I am a %s \n", $self->string_of; }
    Both these methods "expose" object attributes to application code without any risk that they may be changed, and why not? It's up to the library (or class) programmer to decide to offer such methods or not.


    Give a man a fish:  <%-{-{-{-<

      Hello, AnomalousMonk. Thank you for pointing that out. So, is it safe for me to assume that each object attribute should both have a setter and a getter method? If I understood correctly, there has to be a way to update or access an object attribute.

        Not all objects need setter methods. Not all values associated with an object with setters needs its own setter. The getter/setter pair is for things that are both to be accessed by other code outside your object and mutable (that is, updatable) outside the object or from elsewhere in the object.

        If you want an immutable value within your object, you don't have to provide a setter. This won't enforce encapsulation by itself, but it makes it apparent that there's an intent that the value not be updated. Actual enforcement of data hiding is a separate topic.

        If some piece of data should remain entirely private to your object or to the class, then there's no need to create a getter for it, either.

        As for encapsulation within the object, yes it's important that if you have a getter or setter that it retrieves or updates the data in the data structure itself. However, there is some convincing argument on either side of the issue of whether one value's setter and getter should ever access another value within the object directly even within the same object. Some people say direct access to another value in the same object is fine. Others recommend that any part of the object that isn't value B's getter or setter use those methods to access value B.

        Maybe you have a fruit that changes color as it ripens, but it's never going to change what kind of fruit it is.:

        use strict; use warnings; package Fruit; sub new { my $class = shift; my $self = { name => shift, color => shift }; bless $self, $class; }; sub get_color { return $_[0]->{ 'color' }; } sub set_color { $_[0]->{ 'color' } = pop; } sub get_name { return $_[0]->{ 'name' }; } package main; my $obj = Fruit->new( 'apple', 'red' ); my $obj2 = Fruit->new( 'banana', 'green' ); $obj2->set_color( 'yellow' ); use Data::Dumper; print Dumper( $obj, $obj2 ); for my $fruit ( $obj, $obj2 ) { printf "The %s is %s\n", $fruit->get_name, $fruit->get_color; }
Re^3: OOP's setter/getter method - is there a way to avoid them?
by Laurent_R (Canon) on Oct 27, 2015 at 14:54 UTC
    Well, to sort of mitigate what I said previously, if I were to design a class in which all object attributes were to have simple getters and setters, and in which there was no other way to use or modify these attributes than these getters and setters (and the initial constructor), then, my class might still work fine for its intended purpose, but it would likely be poor OOP design. Or it would at best be a use of an OO interface / functionalities to do something else than real OO programming, perhaps, for instance, to simply benefit from data encapsulation and relative persistence from one call to another. And this might be perfectly OK if I don't fool myself into believing that I have created a real OOP design.

    So, yes, accessors might sometimes be harmful if I use them lavishly in order to, in fact, avoid creating a real OO design, but the problem here is not so much my excessive use of accessors, but rather perhaps my own lack of experience in OO programming or my lazy reluctance to actually design a real OO application.

    I have seen at work many Java programs that are not OOP at all: they were just purely procedural programs written in Java (or maybe I should say written in C with a Java syntax) rather than in actual C, presumably just because it is easier not to have to deal with the pitfalls of memory allocation, memory leaks, dangling pointers, segmentation faults and other traps of the C language. Well, why not if it make things easier? But, if you do that, just be aware that it is not true OO programming. My own son (who is preparing a PhD in IT) is frequently using C++ just as if it were pure C, just using some of the facilities offered by the C++ (non-OO) syntax. Again, why not? But this is no OO design.

    Conversely, I am using relatively regularly closures and related FP features to implement data encapsulation and persistence, and creating my own getters and setters, a kind of very light-weight object methodology in a way, but this is also not OO programming.

    So, in brief, I don't think there is anything wrong or even dangerous about accessors in OO programming per se, except that, if I am not careful about what I do, I might simply not be doing true OO programming, but just (cleverly or lazily, it is your draw) using some of the useful tools provided by the OO framework and functionalities in Perl or whichever language in which I happen to be developing.

      Thank you, Laurent_R. After reading the string of comments on this node , the ideas you shared started making more sense. I am starting to appreciate the significance of getter/setter functions though I have to admit that I am still conscious about not breaking an object's encapsulation - even to the extent of not breaking it inside an object. However, I am learning that it is perfectly alright since setter and getter functions reside inside an object, or, even better, a class. Thank you again for sharing your ideas. :)

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (6)
As of 2024-03-29 14:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found