in reply to Re^2: 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?
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.