Perl OO and accessors
dragonchild
created: 2005-11-28 15:28:38
(This is a reply to The Accessor Heresy. I'm doing it as its own meditation because that thread is getting full, plus I feel that this deserves a bit more attention.)

There's a lot of talk about accessors in Perl's OO where there isn't in the OO for other languages1, and for good reason. But, it's not the reason most people think. When you're introduced to OO through Perl, you write accessors. But, that's not because you need to in order to write OO - it's to reduce the possibility of typos when dealing with hash-based objects.

When designing a class, one should be considering how the client will want to use it. In OO, interface is king. Let's take a circle, for example. Disregarding the thorny nest that is a Shapes class hierarchy, we know we want our circle to be able to take a value when it's being built. (We'll get to what that value is in a sec.) We also want to be able to ask it what its radius and area both are. So far, so good. So, we have the following:

Now, when building our circle, we may want to specify the radius or the area. So, instead of a single new(), we may need two constructors - new_from_radius() and new_from_area().

So far, so good. We have a circle object that meets every one of our requirements. Now, let's say we receive a requirement that says "We need to be able to change the circle's size." Ok ... the first idea that comes to mind is "Let's expose the attributes we're using and let the client change them as needed." And, that's the worst idea. The better idea is to provide methods that allow the client to adjust the size. So, maybe something like:

"Hah! You're using accessors now!" Actually, I'm not. An accessor exposes an attribute. I haven't ever mentioned implementation. All I've discussed is the interface, which can be satisfied by any number of implementations. As far as we know, the implementation could be a center point and any point on the circle itself. Or, it could be any three points on the circle. Neither of those implementations have a "radius" or an "area" attribute.

  1. I'm referring to real OO languages, like Smalltalk and Ruby. I'm going to bypass C++ and Java because they're mostly OO, not really OO.

My criteria for good software:
  1. Does it work?
  2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Perl OO and accessors
created: 2005-11-28 16:14:37
"Hah! You're using accessors now!" Actually, I'm not. An accessor exposes an attribute.

What do you mean by "exposing"? So, if you have an implementation that uses the radius as the internal attribute and add a "set_radius" method that just does something like $self->{radius} = shift, you are using an Evil Accessor, but if you have the area as the internal attribute and add a set_radius that does $self->{area} = $PI * shift**2, you have an smart and innocent method? The interface is identical and the user shouldn't know the difference, therefore there's no way of telling, from the outside, whether a method is an accessor or not. Which leads me to the conclusion that all the ranting I hear about accessor methods is nonsense.

Now, what is a true problem is when accessor methods let the user put the object in an inconsistent state. But I call that a plain bug, not accessor evil. For example, let's say that the object stores both the radius and the area (to save recomputing the area, which as we know, is extremely expensive ;-). Then it would be bad to have a set_radius method that only does $self->{radius} = shift, because the area would be left with the wrong value. The set_radius method should be ammended to recompute the area as well.

Re^2: Perl OO and accessors
created: 2005-11-28 18:26:22
Which leads me to the conclusion that all the ranting I hear about accessor methods is nonsense.

I think that's a good conclusion.

-sauoq
"My two cents aren't worth a dime.";
Re: Perl OO and accessors
created: 2005-11-28 16:30:03
When you're introduced to OO through Perl, you write accessors. But, that's not because you need to in order to write OO - it's to reduce the possibility of typos when dealing with hash-based objects.

More importantly, it's so you can maintain the same interface without dictating any internal implementations of any other class that has to offer the same interface. My article in the December 2005 The Perl Review explains more.

Re: Perl OO and accessors
created: 2005-11-28 20:41:20

Presumably you'd also want

get_radius()
get_area()

but then you could use the magic of lvalue subs and tie to let you replace these 4 functions with 2 accessors (it's much less messy in ruby or python)

radius
area

so you could do

$c->area = $c->area / 2;
$c->radius = $c->radius * 2;

Neither of these expose an attribute, despite how it looks. Whether the inplementation actually stores the radius or the area it doesn't matter, neither of these are exposed here, because all interaction is mediated by the methods.

The only thing that is "evil" is unmediated (actually unmediatable) exposure of an attribute. In Java, where this notion of "evil" originates, there is no such thing as mediated exposure of an attribute. The evil in Java is that once an attribute is exposed, it cannot be unexposed and so the interface dictates the implementation forever more.

In languages like Perl, Python, Ruby, Object Pascal and many others it is possible to create something that looks like an attribute but does not force anything on the implementation and so there is no evil. In some of these you can even expose an unmediated attribute and later turn it into a mediated attibute without changing the interface (hence the "unmediatable" above).

So in some languages exposure is evil because the exposure is real and irrevocable but other languages allow you apparently expose or to revocably expose an attribute without forcing anything on the implementation.

Re^2: Perl OO and accessors
created: 2005-11-28 23:07:06
but then you could use the magic of lvalue subs and tie to let you replace these 4 functions with 2 accessors

Using lvalue subs for accessors isn't generally a good idea. For instance, what would you do with $c->area = -42? And, how do you go back and change the interface later to avoid lvalue subs?

-sauoq
"My two cents aren't worth a dime.";
Re^3: Perl OO and accessors
created: 2005-11-28 23:16:28

what would you do with $c->area = -42?

What you do with $c->set_area( -42 )?

how do you go back and change the interface later to avoid lvalue subs?

How do you go back and change the interface later to avoid set_foo methods?

Makeshifts last the longest.

Re^4: Perl OO and accessors
created: 2005-11-28 23:32:20
What you do with $c->set_area( -42 )?

Maybe I'm missing something. Say that I implemented set_area as

sub set_area { 
    my $self = shift; 
    my $area = shift; 
    die "Area must be non-negative." unless $area >= 0;
    # ... 
}
How do you do the same with an lvalue sub?

How do you go back and change the interface later to avoid set_foo methods?

You don't. But, that's okay because you never have a need to.

-sauoq
"My two cents aren't worth a dime.";
Re^5: Perl OO and accessors
created: 2005-11-28 23:36:30

How do you do the same with an lvalue sub?

You return a tied value. Ugly, slow, and doable. fergal did say Neither of these expose an attribute, despite how it looks.

You don’t. But, that’s okay because you never have a need to.

Why then would I ever have a need to go back and avoid lvalue subs?

Makeshifts last the longest.

Re^6: Perl OO and accessors
created: 2005-11-28 23:43:30

I was going to /msg you with "and don't use tie" but you beat me to it.

You return a tied value. Ugly, slow, and doable.
Why then would I ever have a need to go back and avoid lvalue subs?

Perhaps when you decide you need validation without an ugly and slow (but doable) implementation?

-sauoq
"My two cents aren't worth a dime.";
Re^6: Perl OO and accessors
created: 2005-11-28 23:48:07

As Ovid once put it:

Of course, some lvalue fans argue that I can use tie to get my validation back. Great! Now I've lost much of the conciseness I've hoped to gain and I've probably moved the data validation code away from the method in which the maintenance programmer expects to see it but at least I've switched to a slow, confusing and fragile interface!

-sauoq
"My two cents aren't worth a dime.";
Re^7: Perl OO and accessors
created: 2005-11-29 01:21:55

C’mon now, what do you expect me to say? Don’t use Perl5 OO? :-)

Wait. Perl6 is right around the corner.

Or maybe… Have you heard of Ruby?

Seriously, though, things like Tie::Constrained minimise the pain; I’d rather there was a better way, too. In any case, as a user of APIs with lvalue accessors I quite liked them; I haven’t so far actually designed an API with lvalue subs myself, however.

Basically, the ugliness involved in lvalue accessors is just one more thing that makes me go “okay, I have appreciated the beauty and elegance in Perl5’s ultra-minimal OO approach enough, but now I’d like to get some real work done.”

Makeshifts last the longest.

Re^7: Perl OO and accessors
created: 2005-11-29 06:42:42
Nonsense! Ovid's beating a straw man in that quote. Nobody is suggesting that everyday you should bash out lots of awful code directly involving tieing. Would yourself and Ovid also object to exporting values and functions because it involves advanced symbol table poking? Of course not, it's all hidden behind [cpan://Exporter]. Most users don't know how it works at all.

The same goes for lvalue+tie. As I said in another post there's [cpan://Class::Accessor::Lvalue]. Personally I use my own home brew module which lets me say

package Circle;

use Fergal::MM qw( Radius Area );
# this creates
# setRadius, getRadius, Radius
# setArea, getArea, Area
#
# All are created in Circle::Accessors and Circle's @ISA
# is updated to include Circle::Accessors.
# This makes overriding easy.

# now override the Area ones
sub setArea
{
  my ($self, $area) = @_;
  $self->Radius = sqrt($area/PI);
}

sub getArea
{
  my ($self, $area) = @_;
  my $r = $self->Radius;
  return PI * $r * $r;
}

This removes all but one of Ovid's complaints - it's probably slow but if you really need speed then you can just ignore the lvalue interface and use the set/get methods directly. I think I benchmarked it as a factor of 10 slower but how much of your code is accessor calls? If you really need speed, Perl OO is probably not what you want.

Re^8: Perl OO and accessors
created: 2005-11-29 08:48:50

My summary: lots of needless complexity for little or no real gain. I object to that. Strongly. It's not good enough that you can do it. Why would anyone want to? I don't agree, by the way, that what you've shown removes "all but one of Ovid's complaints" but say we take that to be the case. What about the remaining issue? Why would I want to leave bits of a redundant API around with the caveat that, if performance is an issue, they shouldn't be used?

-sauoq
"My two cents aren't worth a dime.";
Re^9: Perl OO and accessors
created: 2005-11-29 11:04:31
lots of needless complexity for little or no real gain.

First off, this is only needlessly difficult in Perl. Python, Ruby and many others support this kind of smart attribute out of the box.

As for the gain, it's in programmer time, readability, simplicity, conciseness and uniformity.

An attribute is essentially a variable with possibly some runtime enforced restrictions so it should be accessible just like any other variable. I can do

$radius *= 2;
$h{radius} *= 2;
$radii[5] *= 2;
so why should I have to do
$c->setRadius($c->getRadius * 2);
why not
$c->Radius *= 2;
?

The existence of something that fundamentally is a variable but that must be accessed in an unnatural way is actually a source of extra complexity. Accessors and mutators are something that are only necessary when the language designer didn't bother include smart attributes.

A variable hidden behind methods causes more code to be written by anyone trying to use that variable and it actually prevents code reuse. For example any function which takes a reference to a variable and alters it can not be resued if the interface for the variable is via an accessor method. Whereas an lvalue attribute will work just fine anywhere a normal variable would have worked.

Occam's razor says we should reduce the number of concepts we have to deal with, lvalue accessors mean we can throw away the awkward not-quite-a-variable attributes. Lispers realised this long ago and introduced generalised variables which give a uniform interface to "cars and cdrs of lists, elements of arrays, properties of symbols, and many other locations are also places where Lisp values are stored".

Why would I want to leave bits of a redundant API around with the caveat that, if performance is an issue, they shouldn't be used?

I think this is backwards. The Area method is the primary and recommended interface. setArea and getArea are the redundant parts of the interface (although they are an essential part of the implementation) and are available in case performance is an issue (they might also be useful if other code is expecting a setter method rather than an lvalue).

So essentially, while the implementation may be complex, it is once-off complexity, hidden even from the author of the class and results in an overall reduction of complexity and increase in productivity. If it leads to unacceptable performance issues you can choose to use to the use the more complex interface in performance sensitive places in which case you still better off than when you were using set/get everywhere.

Re^10: Perl OO and accessors
created: 2005-11-29 17:13:07
First off, this is only needlessly difficult in Perl. Python, Ruby and many others support this kind of smart attribute out of the box.

Well, since this isn't Python or Ruby, we can safely ignore them I think. As for it being "needlessly difficult", well, in my opinion it's just plain needless. When in Rome, do as the Romans. When coding Perl, do it with a method call.

I dispute the notion that doing so would lose programmer time, readability, or simplicity. I don't believe conciseness is a goal in and of itself, but if it is, Perl excels at it in other areas, so you've still got an overall win (by far.) And, as with most things in Perl, uniformity is simply up to the programmer. He must do things in a uniform manner because the language doesn't enforce it.

-sauoq
"My two cents aren't worth a dime.";
Re^11: Perl OO and accessors
created: 2005-11-29 17:34:39
Well, since this isn't Python or Ruby, we can safely ignore them I think.

*blink blink* Huh?!? When is disregarding another language's strengths a good thing? Or, is the idea "We can't do it, so it can't be good"? FYI - the Perl6 object system is heavily influenced by Ruby, Python, Smalltalk, and Lisp. In fact, as stvn has put it, the P6 OO system is meant to take the best elements from the best-of-breed languages and meld them into something that doesn't suck.

Yes, Perl is an amazing language. By far, it's the best all-purpose language out there, with the CPAN being a major component of that. This doesn't mean that it can't be improved. If it's to be improved, one needs to find languages that do things better, then borg those features into Perl. That's what Perl has always done from the very beginning. And, that's what Perl6 is doing. Not just OO, but macros and grammars and function signatures and a lot of other things that have been seriously missed in Perl5.

Now, you may be thinking "I do perfectly fine without X in Perl." And, yes, you're right - you do perfectly fine. But, have you ever dipped into another language that didn't have hashes or regexes or lists that DWIM? In other words, have you ever written in C, Java, or Javascript*? After working in Ruby for about a month, it's like pulling teeth to work in Perl5's OO. It feels clunky, wordy, and just plain too complicated. It literally feels like I'm back in ANSI C or Pascal. Heck, coming back to Perl5 from Ruby feels that way in nearly every sense. As a few people have put it on the Perl6 langauge list, Ruby is the closest we're getting to Perl6 until Perl6 is out.

*: Before you say that JS has hashes, it doesn't if someone changes Object.prototype anywhere at anytime, like the oft-used prototype.js library does.


My criteria for good software:
  1. Does it work?
  2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re^12: Perl OO and accessors
created: 2005-11-29 21:55:42
When is disregarding another language's strengths a good thing?

Gee... just about anytime you aren't discussing language design or making comparisons. Which, in case you hadn't noticed, we weren't. We were talking about good ways to do things in Perl . (And, by "Perl", I mean the one people use for serious work now; not 6.)

Now, you may be thinking "I do perfectly fine without X in Perl.

You seemed to have missed the mark here. There are plenty of things from other languages that I miss in Perl. But I don't try to jerry-rig them in Perl just because they aren't there. I take the pragmatic approach, the path of least resistance, and do it the way that makes sense in the language I'm working with. (And that's a fair summary of the node to which you were replying.)

In other words, have you ever written in C, Java, or Javascript?

For the record, sure. All three. Far more in C than Java or (snort) JS. C++ too. And quite a bit of Python at one time. And some TCL. And Lisp. CLIPS. Fortran. Pascal. Basic. Various shells, of course. A tiny bit of Ada once (which, thankfully I've forgotten everything about except its god-forsaken verbosity.) And, more recently, yes: Ruby; which is, I agree, very nice indeed. (Nice enough, in fact, that by the time Perl6 come into its own, I'll probably have to be converted from Ruby.) But still, I won't be getting entirely away from Perl5 anytime soon.

-sauoq
"My two cents aren't worth a dime.";
Re^10: Perl OO and accessors
created: 2005-11-29 17:33:51
Howdy!

An attribute is essentially a variable with possibly some runtime enforced restrictions so it should be accessible just like any other variable.

That is needlessly restrictive, and the conclusion does not follow from the premise. Suppose you have a timer that is counting up/down. You may have, as an "attribute", the elapsed time/time remaining. Should that look like a variable? Or should I call a function to retrieve that data? Suppose the data isn't actually stored in the object, but is an external resource?

Yes, it can be aestheticly pleasing to be able to write code that avoids overt function calls, but the flip side is that you then have derived attributes that look different from innate attributes, or you have to maintain the derived attributes in sync with changes to the innate attributes, further confusing the matter. I take it to be somewhere between "better" and "no worse" to have to make explicit method calls to set/get attribute-like properties of an object.

yours,
Michael
Re^11: Perl OO and accessors
created: 2005-11-29 19:17:35
An attribute is essentially a variable with possibly some runtime enforced restrictions so it should be accessible just like any other variable.
That is needlessly restrictive, and the conclusion does not follow from the premise. Suppose you have a timer that is counting up/down. You may have, as an "attribute", the elapsed time/time remaining. Should that look like a variable?

So by changing the premise (widening the meaning of "attribute") you find that the conclusion no longer follows. That's hardly a surprise.

Of course the timer shouldn't look like a variable because it doesn't behave like a variable. Trying to put a variable-like interface on something that doesn't behave like a variable is just confusing and a bad idea.

My definition was needfully restrictive - in order to reach my conclusion. If your idea of an attribute includes things that don't behave like variables then please run

perl -pe 's/(attribute)/behaves-like-a-variable-$1/g
on my previous comments.
Re^12: Perl OO and accessors
created: 2005-11-30 10:30:30
Howdy!

It's not that I "changed" your premise so much as I disagree with your premise (and, implicitly, the conclusions you draw from it).

In the context of OO and objects, I take "attribute" to refer to data contained within an object. Quite often, it is a simple variable-like thingy, but it is not necessary that fetching the value of an attribute be idempotent. If an object has behavior, and that behavior is autonomous, then my scenario does hold.

Thinking in shell terms, consider the variables like RANDOM, SECONDS, etc. They change out from under you in a similar manner.

Besides, $o->foo isn't a variable. It's a CODEREF. If it's an lvalued CODEREF, then it can masquerade as a variable, and that can be useful, or could be if lvalue subs weren't so limited in what they can do (not to mention making it impossible to use the debugger). ${$o->foo} *would* be and look like a variable, but then you have those extra curly braces cluttering things up. *That's* what you should be comparing, and I find the aesthetics unfavorable.

yours,
Michael
Re^13: Perl OO and accessors
created: 2005-11-30 11:42:56

So we have different ideas of what attribute means, fine. I absolutely agree that my conclusion don't hold for your definition of attribute but I don't understand why you think it fails for my definition.

I still maintain that if the attribute behaves exactly like a stored piece of data then you should give it the same interface that we use for every other stored piece of data in the system. I haven't heard a single reason why this is undesirable in principle or an argument why set/get methods provide a superior interface.

Thinking in shell terms, consider the variables like RANDOM, SECONDS, etc. They change out from under you in a similar manner.

They're an example of something that should never have been a variable in the first place. In everything besides shell they are functions. I'd guess the reason they're variables in shell is because there was no other way to do it way back when they were introduced. I can't see any advantage to having them as variables. Can you?

Besides, $o->foo isn't a variable. It's a CODEREF.

It's neither a variable nor a CODEREF, it's a method invocation but that's not important. The point is that using lvalue and tie, it presents the interface of a variable and can be used anywhere that a variable previously might appear. So I can do

$x++;
$o->foo++;
$x=7
$o->foo = 7;
chomp($x);
chomp($o->foo);

sub truncate
{
  my $string = shift;
  if (length($$string) > 10)
  {
    $$string = substr($$string, 0, 10);
  }
}
truncate(\$x);
truncate(\{$o->foo});

I don't understand your comments about ${$o->foo}. Why should I be comparing that? That brings us back to exposing a part of the object directly plus it's ugly as you point out. The fact that it *would* be a variable is not only irrelevant but actually a disadvantage. I'm not saying that $circle->Area *is* a variable just that it has the exact same interface and behaviour as a variable even though internally $circle actually stores the radius not the area.

Re^14: Perl OO and accessors
created: 2005-11-30 16:13:59
I can't see any advantage to having them as variables. Can you?
  $ echo "'$RANDOM' is a random number"
Perl --((8:>*
Re^15: Perl OO and accessors
created: 2005-11-30 19:58:16
Is that supposed to be an argument for having them as variables? If Bash had a function named random rather than a variable then you could do this
echo "'$(random)' is a random number"

If the extra ()s are too much of a burden then the logical conclusion is that everything should be a variable just to make it easy to do in string interpolation.

The principle of least surprise means that you shouldn't make something look like a variable if it's not going to behave like one. For example

MINUTES=5
echo $MINUTES
# 5
SECONDS=5
echo $SECONDS
# 1276

How can that be a good thing? There's not even a warning.

Also, having variables that aren't leads to run time errors that could have been caught at compile time. If random was a function, attempting to assign to it would be an error (at runtime in bash but at compile time in perl).

Re^3: Perl OO and accessors
created: 2005-11-29 03:47:43
. . . what would you do with $c->area = -42?

use Math::Complex;

;-)

After Compline,
Zaxo

Re^3: Perl OO and accessors
created: 2005-11-29 06:09:26

That's why I said lvalue subs and tie.

The technique involves returning a tied lvalue which then calls a setter method with the new value. All this can be hidden behind a use statement. See Class::Accessor::Lvalue for an example.

Re: Perl OO and accessors
xdg
created: 2005-11-29 00:36:08

I haven't fully gone through the other thread, but I think this issue -- at least for Perl -- really needs to be be distinguished between accessors and mutators used within the class package and those used outside the class package.

Within the class package, accessors and mutators are heavily used for typo avoidance -- or rather, compile-time typo checking. That's a straightforward tradeoff of safety for speed and fair enough. (And yes, inside-out objects address some of that, with yet other tradeoffs.)

Outside the class package, the issue of whether accessors and mutators violate encapsulation, etc., is what becomes the religious war. The "problem" for Perl is that most accessors and mutators -- even for internal use -- are declared as public functions for various reasons

  • it's less complicated than checking caller
  • it's easier to type/read than calling as a private function -- as $self->foo vs _foo( $self )
  • historical convention and accessor generator modules have taught people that's how to do it.

I generally feel that your comments about interfaces make sense. But whether you call them accessors or call them interfaces, the issue is the same. By tradition, Perl programmers frequently implement accessors and mutators as part of the published interface regardless of whether it really makes sense to do so from a "good design" standpoint. This is what leads to the sniping about "glorified structs" and so on. To some extent, I agree -- a programmer's convenience to avoid typos shouldn't be the driver of a public interface.

Personally, I think that the distinction between an accessor and an interface is usually pretty minimal. This isn't to say that all internal properties should be exposed, but querying an object's state as expressed by its properties is frequently a necessary part of an interface. If the internal representation changes, the accessor becomes an interface that preserves compatibility.

The stylistic/religious argument question as I see it is more about the mutability of properties once an object has been created -- whether the properties should ever be changed directly independent from the behavior of the object. To use the circle example, does a circle "change radius" or "change size"? One is a statement about a property and one is a behavior. (For this trivial example, they are mathematically equivalent, but not all cases are so trivial -- this is the peril of simplistic analogies.) On balance, I think that defining this interface in terms of the behavior makes for better design. E.g. this function defines a behavior and it can be parameterized in several ways regardless of the internal representation.

$circle->resize( radius => 2     );
$circle->resize( area   => 12.56 ); 

However, no rule should be absolute, and I'm sure there are cases where breaking encapsulation and directly manipulating properties makes for a better/faster/whatever implementation. That said, the principle that I think makes the most sense for mutators is a paraphrase of Bill Clinton's statement on abortion (and apologies for bringing up politics should that offend or distract anyone): Mutators should be safe, legal and rare. (At least as part of the public interface.)

As a related point, this is one reason why I've decided I'm not really in favor of the combined getter/setter style of accessors that are frequently seen in Perl. In addition to the ambiguities in usage that others (e.g. PBP) have noted, I think they make a poor default assumption that the interface for querying state should be the same one as for modifying state. Instead, for the circle, I'd rather see something like this:

# Querying state -- which is the accessor and which are the interfaces?
$circle->radius();
$circle->diameter();
$circle->area();

# Modifying state -- interface potentially defines multiple acceptable forms 
$circle->resize( radius     => 3               );
$circle->resize( match_area => $another_circle );

This keeps the user interface at the center of the design rather than having it be just a consequence of accessor generators.

-xdg

Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re^2: Perl OO and accessors
created: 2005-11-29 05:40:48
Within the class package, accessors and mutators are heavily used for typo avoidance -- or rather, compile-time typo checking.
Considering that Perl does look up of methods at run-time, what checking is done at compile-time?
(And yes, inside-out objects address some of that, with yet other tradeoffs.)
Nowadays, I usually use inside-out objects, and that means I don't write accessor all that often. I don't use objects if I want C-type structs, hashes will do fine in that case. For other cases I only provide methods to the outside world for those cases where the outside world might want to inspect (or set) some state of the object. This may be an accessor, but the outside world neither knows, nor has the need to know.

I never use accessors to let a class get at its own data. IMO, there's no point in adding the overhead of calling a method to get a value from a hash, and I prefer the compile-time errors I get from making typos when accessing the data instead of the run-time errors when typoing a method name.

For me, the existance of an accessor means that I intended the outside world to set, or look at a state. And that I hence should be careful when redoing the internals.

Perl --((8:>*
Re^3: Perl OO and accessors
created: 2005-11-29 07:54:22
I never use accessors to let a class get at its own data.

That's an interesting statement. I actually always use accesors when dealing with attributes to allow for subclasses to override how they wish to deal with an attribute. But, I tend to work either with datastructure-type classes like Tree where the algorithms of the class need to be separated from the datastorage of the class or with templating engines like Excel::Template where a given node doesn't know if it actually has the attribute or not. (Its parent might have the attribute, so it has to ask a separate context object where the attribute actually has a value.)


My criteria for good software:
  1. Does it work?
  2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re^4: Perl OO and accessors
created: 2005-11-29 08:10:42
I actually always use accesors when dealing with attributes to allow for subclasses to override how they wish to deal with an attribute.
One of the reasons I use objects is because of encapsulation. Nothing, but the class itself, is allowed to have behaviour that's depending on the class' implementation. And that includes masking accessors. A subclass is not even allowed to know what is an accessor and what isn't.
Perl --((8:>*
Re^4: Perl OO and accessors
xdg
created: 2005-11-29 08:26:02
I actually always use accesors when dealing with attributes to allow for subclasses to override how they wish to deal with an attribute.

But that brings us back the the whole public/private/protected/friend issue of encapsulation of data in Perl objects (or lack thereof). If what Perl Mouse describes as a class's "own data" is private data specific to the implementation, then it's perfectly acceptable not to use an accessor -- though that may limit the ability of a subclass. (Not all classes need to be subclassable.) If subclassing is desireable, then it may make sense to implement "protected" accessor/mutators that can only be called by subclasses -- at least for data which pertain to general properties of the object which would be relevant to subclasses.

One benefit of inside-out objects for OO purists is that such subclassability must be made explicit. By default (under the most commonly-seen implementations, anyway), all properties are lexicals and thus private and can be used directly. Accessors/mutator need only be written when it makes sense for the interface, whether that is fully public or only for subclasses.

Any inside-out object generating module that automatically defaults to creating accessors/mutators for all properties defeats this benefit of inside-out objects towards encouraging "good" design. Class::Std

-xdg

Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re^5: Perl OO and accessors
created: 2005-11-29 14:27:39

Your use of the phrase "automatically defaults" regarding CPAN://Class::Std must have some meaning with which I was previously unaware!

Re^5: Perl OO and accessors
created: 2005-11-29 15:21:30
Howdy!

Ummm...to expand on chromatic's remark, Class::Std *does* provide a way to have it generate accessors and mutators on the fly, but neither is done by default.

I'm glad you put quotes around "good" there. Creating accessors and/or mutators is not, per se, good or bad. It may be beneficial to start out with accessors and mutators in place to limit the number of places where the attributes are directly messed with. Doing so gives you the ability to change the storage mechanism without breaking your API, even within the package itself.

I also use "attribute" to include anything that looks like an attribute even if it isn't directly stored as a value (such as radius vs area for the famous circle). It's a Good Thing for the API to blur the distinction between attribute-like things that are computed versus those that are directly stored, as the user of that class has no Need To Know how the data is stored within the object.

yours,
Michael

perlmonks.org content © perlmonks.org and Aristotle, chromatic, dragonchild, fergal, herveus, itub, Perl Mouse, sauoq, xdg, Zaxo

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03