http://qs321.pair.com?node_id=1146239


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

My question is, are setters and getters still used these days in Perl programming?

I've followed the responses to this thread with interest. The sum total of them seems to be: getters and setters are okay because they are internal to the class.

I say, for the most part, that is phooey.

Well written classes for most objects should never need setters and getters for their internal attributes.

An example: A BankAccount class.

One way to write that it to have a getter and a setter for the balance attribute. The code charged with processing transactions receives a transaction for a particular account number; creates an instance (proxy) for that account; calls the $act->get_balance() method to retrieve the current balance; adds or subtracts the transaction amount depending if it is a deposit or withdrawal; then calls the $act->set_balance() method to store the revised amount. This is WRONG!

The transaction processing code should: instantiate the account, and the pass the transaction object to $act->transact( $trans ) method. That method then performs all the required checks and balances before finally -- assuming all is well -- modifying the balance attribute to reflect the transaction.

Well designed classes should (almost) never need to expose their internal attributes to the outside world via setters & getters. Calling code should not query current values, modify them and then set them back; it should pass the information detailing the change to an appropriate method that will perform that task.

That's why I'll never be a Mo(o)(u)(se) user. Pretty much the only thing those modules do for you is automate the construction of setters and getters along with validating the values passed to them. But as (almost) no class should ever require getters & setters that is all redundant.

As for validation: If all values entering the class come in via 'do something useful' methods, by the time the objects attributes get around to being modified, the logic of those methods has already ensured that the values being set into attributes are valid; so further indirection and validation is both redundant and noxious.


With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
In the absence of evidence, opinion is indistinguishable from prejudice.
  • Comment on Re: OOP's setter/getter method - is there a way to avoid them?

Replies are listed 'Best First'.
Re^2: OOP's setter/getter method - is there a way to avoid them?
by mr_mischief (Monsignor) on Nov 04, 2015 at 18:36 UTC

    What you're saying, in short, is to take responsibility for implementing the actions taken on your object rather than foisting that responsibility onto code outside the object?

      What you're saying, in short, is to take responsibility for implementing the actions taken on your object rather than foisting that responsibility onto code outside the object?

      Yes, but also more than that.

      The use of accessors & mutators does not necessarily imply that. For example, my BankAccount example might be implemented something like this:

      sub new { my %account; ... return bless \%account; } sub getBalance { my $self = shift; return $self->{balance}; } sub setBalance { my( $self, $newBalance ) = @_; $self->{balance} = $newBalance; return; } sub transact { my( $self, $amount ) = @_; my $newBalance = $self->getBalance() + $amount; if( $amount < 0 ) { ## withdrawal if( $newBalance > 0 ) ) { $self->setBalance( $newBalance ); } elsif( $newbalance > $self->overdraftFacility() ) { $self->setBalance( $newBalance ); } else { die 'Overdraw attempt'; ## raise exception; allow caller t +o deal with the problem } } else { ## deposit $self->setBalance( $newBalance ) } return $newBalance. }

      But the point to note here is that getBalance() & setBalance() serve no purpose. There is no purpose in performing any further validity testing within those methods as they should never be called from outside; and whenever they are called from inside, all validity checking required or possible should have already taken place.

      So every use of those methods can be directly and correctly substituted with inlined, direct access to the object attributes; thus they are pure overhead for no benefit; and with the very great downside of breaking encapsulation by exposing the internal attributes to the outside world via the very accessors that are apparently intended to ensure it!

      The same applies to pretty much all other accessors and mutators, with the very rare exception of when it makes sense to have classes of objects that are nothing more that bags of associated variables. And I do mean, very rare.

      There are only two arguments that even vaguely make sense for the provision of accessors:

      • The use of accessors -- internal to the class -- simplifies the (potential future) process of changing the underlying storage of the class. Eg. Moving from a hash-based to array-based (or vice versa) class implementation.

        Sounds cool; but in nearly 30 years of writing and using OO classes; I've never seen an occasion where this was necessary without it also requiring so much other redesign that the savings attributed to the use of accessors would simply be lost in the noise of the overall re-work effort.

        In the greater scheme of things, imposing a recurring, unnecessary, and avoidable fixed overhead on every use of a class in every application that uses or reuses it, in the forlorn hope that you might gain some insignificant benefit from it at some point in the future that in 99% of cases will never arrive is the absolute height of the programmer arrogance that their time is more valuable than that of their 10s, or 100s, or millions of users.

        The arrogance that it is worth the recurring imposition of overhead upon every run; affecting every program that uses the code; and every user that uses those programs; just in case it might save them (the programmer) a few minutes at some unspecified point in the future.

      • That they simplify the process of extending (subclassing) the class.

        I've seen this argument put forward, but never seen any evidence that it is so. It seems to me to be just as easy to subclass a class that doesn't use accessors as it is one that does. And further more, the absence of the additional overhead of accessors in the parent class mean that subclasses also benefit.

      To date, and I've been arguing this point for most of my career, the provision of externally accessible accessors and mutators is a direct contravention of the design goals and aspirations of OO design. And the use of internal use only accessors and mutators -- where the language allows this -- is pure avoidable overhead with only a theoretical potential benefit that in 30 years I've never seen realised.

      In compiled languages with decent optimising compilers; that overhead is minimal as the accessors and mutators get in-lined at compile time; but in a interpreted languages without that benefit and without a mechanism to prevent those accessors being used externally to the class; they are not just overhead, but severe code-smell.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
      In the absence of evidence, opinion is indistinguishable from prejudice.

        Using the bank account example, though, what if I just want to check my balance? What if I just want to check the date of account creation, or verify my account number? No actual mutation is necessary, but it would be handy to access those values. There wouldn't be a need to mutate those directly, but accessing them without making a deposit or withdrawal should be possible. I suppose you could have a method called query_balance that returns the balance and also updates a log of the query.