in reply to Perl OO and accessors

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 run-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

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.

Update: Yes, it should have been run-time, not compile-time, on the typo checking. Thank you, Perl Mouse.

-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.

Replies are listed 'Best First'.
Re^2: Perl OO and accessors
by Perl Mouse (Chaplain) on Nov 29, 2005 at 10:40 UTC
    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:>*
      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?
        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. <cough>Class::Std</cough> (whoops -- misread the docs)

        -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.

        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:>*