Since Java still gets managers all hot and bothered, I decided that I would dive into yet another programming language (when will I ever learn?). Playing with Swing, I discovered that mutators have void return types. That seems like a waste. Why not return the object? Since chained mutators seem to be something that no one can agree on, I've just decided to come down on the "I likes 'em" side of the fence. Which leads me to my problem.
You cannot overload Java methods by changing the return type.
After that serious let down, I realized I would have to change method names to get what I want. Now, instead of this:
JPanel pane = new JPanel();I can do this:
ChainedJPanel pane = new ChainedJPanel();I have a feeling that this will one of my less popular hacks, but with better exception handling and polymorphic dispatch based on method signatures, it seems that Java is a better fit for this idiom than Perl.
If you can deal with how painfully slow JavaJunkies is, you can read about my attempts here.
Re:What....are.....you.....doing?!
jmm on 2003-06-17T16:16:06
So, if you can code real fast this way, does that make you a chain smoker?It might be nice to have a syntax that factored out the object from a sequence of method calls to the same object. Something like:
(@return list) = $object-->>(
-> meth1(args),
-> meth2(other args),
-> meth3(la la la)
);Reading a series of method calls carefully to verify that they are really all being applied to exactly the same object (or discovering a typo the meant that they were' all being applied to the same object) is the sort of work that I'd rather let a computer do for me, or rather, let have a computer make unnecesary.
Re:What....are.....you.....doing?!
Ovid on 2003-06-17T16:19:09
What I'm accomplishing is grouping things together that logically belong together. Plenty of times I've seen cut-n-drool programmers slap some code in the middle of a chunk that shouldn't be split up only to break things and scratch their heads trying to figure out why. This makes it tougher to do that.
No offense, but I don't buy the "confusion down the road" argument. Why is this confusing? Because it's new? No one has a problem chaining accessors and they do it every day in Java. The first time I saw chained mutators, I saw what was going on and thought that it was a nice tool. Accessors return different objects and mutators return this. It's pretty straighforward.
As for whether or not it's intuitive, there is very little in any language (computer or spoken) that's intuitive. I can't tell you how many times I've read in computer books something along the lines of "ternary (or unary) operators confuse some programmers so you may want to avoid them". That's FUD. Would you really want to hire someone who thinks that ++variable is confusing? If you want confusing, consider context in Perl. Many of the rules seem arbitrary and are merely a matter of rote memorization. That's confusing. "Mutators return the object" is the only thing that needs to be remembered with chained mutators. That's simple.
When I was doing mainframe programming, there was a huge section in our programming standards manual about COBOL keywords we weren't allowed to use because they weren't often used. That's just a silly self-fulfilling prophecy and I have first hand experience fixing stupid bugs caused by the contortions some programmers went through just to avoid some of those keywords.
So restating all of this: the pro (to me) is that logically grouped constructs are distinctive. If I'm refactoring or debugging some code, it's immediately apparent that a chained mutator construct is a logical group that probably shouldn't be split up. This, like indenting to show scope, is not strictly necessary, but is very convenient for a quick visual understanding of the code (and when I first went from BASIC to C back in the 80s, I didn't grok indentation, either). I think that anything that improves visual comprehension of code is a good thing.
So what are the cons? I hear some arguments against this in Perl and those arguments go both ways. Much of those arguments, however, struggle with Perl's lack of structured exceptions. That argument goes away with Java. As for being unintuitive or confusing, most who come from procedural languages would argue that about the signature of the main() function in Java programs (public static void main(String[] args)????). It's merely a matter of learning it.
Now if you can point to real problems with this syntax, I'd love to hear them -- that's why I posted
:) Re:What....are.....you.....doing?!
iburrell on 2003-06-17T19:10:31
The main problem with chained mutators is that it requires cooperation of all the mutators to work properly. It adds extra complexity to all of the mutator implementation to gain a little convenience for the caller.Also, sometimes it makes sense for mutators to return something. For example, returning the previous value can be useful. Or if the mutator constructs an internal object, then returning that object might make sense. In Perl, the lack of exceptions frequently means returning error indicators.
It is possible to introduce mysterious bugs when the wrong object is returned. What happens when the mutator returns a different object? If the returned object is of the same class, then no errors will be thrown. If it is a different class, then Java's type checking will usually catch it. But in Perl, it is quite possible to continue without any errors but getting the wrong behavior.
Re:What....are.....you.....doing?!
chromatic on 2003-06-17T20:23:33
It adds extra complexity to all of the mutator implementation to gain a little convenience for the caller.Surely that's a good thing! When you make it easy for the implementor at the expense of the user, you end up with something like Ant where you have to write XML. I'd much rather use Module::Build or even ExtUtils::MakeMaker — for all their faults (and the former has many fewer faults than the latter), they're easier to use than either writing my own Makefile or writing an XML file.
Without first-class support for cascading, a simulated cascade (i.e., a chain) is hard to distinguish from a gross violation of the Law of Demeter.
Re:Chaining vs. Cascading
Ovid on 2003-06-20T18:55:59
I don't really want to try and describe this in Smalltalk terms. This isn't Smalltalk and shouldn't be viewed as such. Even though the idea appears to come from Smalltalk, it's simply not the same thing as a cascade. That seems like saying for (@array) shouldn't be used in Perl because it's not an OO iterator. In this case, it would not be helpful to insist on a full implementation of the GoF Iterator pattern. It's certainly an iterator, but it's implemented in a Perlish way. The case of the chained mutators in Java, we have a useful idiom being implemented in a Java-ish way, if you will.
As for violating the Law of Demeter, I just don't see it. The Law of Demeter is about decoupling your classes. Since chained mutators don't inherently rely on any other class, they don't violate said law. Perhaps you're referring to the calling code as violating that law, but if they're allowed to call the original mutator without violating the Law of Demeter, then subsequent calls, whether against the original variable or the return value don't seem to be a problem.
Maybe I just don't understand the Law of Demeter well enough. I pulled out some books to get a better understanding, but I still don't see what you're getting at. Could you explain?
Re:Chaining vs. Cascading
dws on 2003-06-20T20:19:54
Put simply, the Law of Demeter (LoD) says "You should only talk directly to your friends, and not to their friends." This reduces coupling, and simplifies testing. To test your code, the most you have to do is provide mock objects for your code's direct collaborators.Violations of the Law of Demeter are characterized by chains of method calls, where the target object changes along the way as the code reaches through its friends and into their friends (and perhaps into their friend's friends). Lots of coupling, and hard to unit test.
Since chained mutators don't inherently rely on any other class, they don't violate said law.
But can a reader tell that there's no violation without reading each and every method invoked in the chain to make sure that each method really is a mutator? No. When I encounter one of these constructs, I'm forced to do a lot of extra reading, just to make sure that somewhere in the middle some other object isn't injected into the chain.
With first-class support for cascades, this isn't an issue. But by trying to fake a cascade (and that really is what you're trying to do), you're saying "trust me" to the code's readers, forcing those who don't trust you into extra work.
Experience has taught me not to trust chains of method calls.