Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Revelation (4242)

Revelation
  (email not shown publicly)

Journal of Revelation (4242)

Monday June 16, 2003
12:10 AM

Method Chaining (Regarding A Recent Perlmonks Discussion)

[ #12867 ]
I'd like to discuss why method chaining is bad for widely distributed modules in response to this discussion. I've decided to put it in its own node, because I believe that the topic of why NOT to use it could be very important. I hope that if I share my rationale of the use of method chaining and how it hurts large packages, people will reconsider their belief in that technique. To begin with, it'd be nice to define method chaining- my current definition is chaining a series of methods that work off of a single object. It's used most often for attributes to an object, but I guess it could be used to actually call methods. It's major advantage is that it allows for great brevity, and demonstrates the interconnectedness of the attributes, as well as puts them in a single place.

All of these are good things, and as demerphq said, it seems to make sense to write:

print Data::Dumper->new([$foo],[qw(foo)])->Indent(2)->Purity(2)->Dump();

instead of:

print do{my $d=Data::Dumper->new([$foo],[qw(foo)]); $d->Indent(2); $d->Purity(2); $d->Dump()};

however, this is a rather short sighted consideration, when one looks at the cons of method chaining. If our criteria is as said "easy things should be easy, and hard things possible," we must adopt some sort of standard to make maintanance programming (one of those easy things) and extending packages (another one of those easy things) easy. Method chaining betrays the later, and even makes the former rather hard.

Consider even the previous example. A perl programmer, who is familiar with Data::Dumper, would automatically assume that you're modifying multiple attributes of the dumper object. However, a maintanance programmer, or basically anybody not familiar with it, would infer that the programmer is changing the purity of the indent, not the greater object. This is because programming standards (think of them as extended but unenforced grammer, the degree of scrictness after use strict;,) would dictate that as we go further from the left, we get more and more specific, that we are modifying the attribute.

But who cares about standards? One shouldn't have to contort oneself to fit into some arbitrary coding scheme when there is no reason to. We're basically selecting a design that isn't common. The problem with that is twofold, first that the majority of people would not consult a reference for such a small item of concern and they would further probably assume that the common standard is being used, and second that the very standard used is used for a reason...

It's the sad truth that method chaining leads to problems for people who don't read the documentation, but that it also leads to inflexibility and problems for maintanance programmers. The inflexibility is that of being forced to have seperate accessors and mutators, because accessors have to return the data accessed, wheras mutators are now returning the object modified. This can be delt with by just checking if $_[1] is defined and sending $self if it isn't, so I don't see much of a problem with it. However, method chaining at that moment makes even less intuitive sense, because I'm using the same method with completely differant results, one of which is the parent object, and the other of which is an internal part of that object.

But I can cope with that. No big deal. The second problem, however, is something I can't deal with. Consider, from perldoc:

$object->fullname('thadeus');

# But my system's gotten more complicated.
# so now I'm going to make fullname into an
# object of its own.

$object->fullname('Thadeus Barton')->christian('Thadusky');
$object->fullname('Frankeus Muss')->hobbessian('fransko');


You've created the maintanance/extensibility nightmare of all nightmares! Now it's necessary to document what is a chained method and what isn't. You've also made it possible to do: $object->size('large')->fullname('Frankeus Muss')->hobbessian('fransko');

It's a sad day for whoever has to work with that code. So we have to make a decision between the two? If there's a good chance we won't need to extend our work, why do so? Although being able to transparently extend objects (change how it's implemented) without changes usage is one of the major reasons people encapsulate data, it can be sacraficed for DWIM. To me, getting rid of that layer for a few silly strokes is futile. Especially when there's a perfectly valid alternative- all of this, because you were to lazy as a programmer to just have a single set method, which would be the only thing needing documentation:

print my $d=Data::Dumper->new([$foo],[qw(foo)])->set(Indent => 2, Purity => 2)->dump(); # If brevity is all too necessary.

my $d=Data::Dumper->new([$foo],[qw(foo)]); $d->set(Indent => 2, Purity => 2); print $d->dump();


At least this way you're not tredding on OO dogma.
Gyan Kapur
gkapur@myrealbox.com
The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • Method chaining is fine as long as it's clearly documented and consistent within a module. I would be hauled off to the looney bin if I were to make any kind of global recommendation, however.

    In my new Alpaca book [oreilly.com], I argue that for a getter/setter method, there are arguments in favor of any one of four return values from the setter method:

    • a simple true/false status (yes I was able to set this),
    • the newly set value, turning the setter into an immediate getter as well,
    • the previous value (ala umask and single-arg select), allowing flip-flop settings trivially, or
    • the object itself, allowing for chained settings.
    For anything but the first, a die has to be used to signal error.

    As long as it's documented, it's fine. Don't demand consistency. Any one of these four returns is fine.

    --
    • Randal L. Schwartz
    • Stonehenge
  • by djberg96 (2603) on 2003.06.16 8:41 (#21106) Journal
    See Want.pm. Anyone who wants to support method chaining as a regular feature of their module ought to be using this. No more of this "do I return $self or a $value" nonsense.
    • That doesn't really help too much. Want.pm can't distinguish between the different objects that could be wanted, getting a program to think metacognitively at that complexity is impossible. (If you've invented AI *that* good, Terminator 3 will soon become a reality...)

      I could be wanting the object returned by $object->head, because you've made the head into its own greater object to allow for more methods, or I could want the object to be a chained method. How can you tell the difference? How can a
  • Smalltalk (Score:2, Interesting)

    Smalltalk returns self by default if there is no explicit return value. I think that's part of the reason why well written Smalltalk programs look just beautiful.
  • The real problem is that you're expecting your set methods to return the value that you just set (at least in your fullname) example. So you now you have a combined get/set method, which is icky.

    If your set methods are are clearly distinct from your getters, then returning $self from setters makes perfect sense in _all_ cases.