views:

368

answers:

5

When I am making methods with return values, I usually try and set things up so that there is never a case when the method is called in such a way that it would have to return some default value. When I started I would often write methods that did something, and would either return what they did or, if they failed to do anything, would return null. But I hate having ugly if(!null) statements all over my code,

I'm reading a re-guide to ruby that I read many moons ago, by the pragmatic programmers, and I notice that they often return self (ruby's this) when they wouldn't normally return anything. This is, they say, in order to be able to chain method calls, as in this example using setters that return the object whose attributes they set.

tree.setColor(green).setDecor(gaudy).setPractical(false)

Initially I find this sort of thing attractive. There have been a couple of times when I have rejoiced at being able to chain method calls, like Player.getHand().getSize() but this is somewhat different in that the object of the method call changes from step to step.

What does Stack Overflow think about return values? Are there any patterns or idioms that come to mind warmly when you think of return values? Any great ways to avoid frustration and increase beauty?

+1  A: 

I don't agree that methods should never return null. The most obvious examples are from systems programming. For instance, if someone asks to open a file, you simply have to give them null if the open fails. There is no sane alternative. There are other cases where null is appropriate, such as a getNextNode(node) method, when called on the last node of a linked list. So I guess what these cases have in common is that null represents "no object" (either no file handle or no list node), which makes sense.

In other cases, the method should never fail, and there is an appropriate exception facility. Then, I think method chaining like your example can be used to great effect. I think it's a bit funny that you seem to believe this is an innovation of the "Pragmatic Programmers". In fact, it dates to Lisp if not before.

Matthew Flaschen
Can you give an example of method chaining in Lisp? I haven't seen such a thing in Lisp (except in Clojure, but Clojure is a recent language). It probably comes from Smalltalk: it has special syntax for this (semicolon).
Jules
I hope I didn't give the impression of attributing method chaining to the PP. That's just where I saw it first. I notice that my friend Jon often uses object-oriented programming: what a genius!
Ziggy
Just my 2cents, but I would like the open file code to throw an exception in that case. Unless it is higher level code.
Stormenet
A: 

It's confusing to me. OO programming languages need Smalltalk's semicolon:

tree color: green;
     decor: gaudy;
     practical: false.

obj method1; method2. means "call method1 on obj then method2 on obj". This kind of object setup is very common.

Jules
+3  A: 

In my humble opinion, there are three kinds of return-cases that you should take into consideration:

Object property manipulation

The first is the manipulation of object properties. The pattern you describe here is very often used when manipulating objects. A very typical scenario is using it together with a factory. Consider this hypothetical creation call:

// When the object has manipulative methods:
Pizza p = PizzaFactory().create().addAnchovies().addTomatoes(); 
// When the factory has manipulative methods working on the 
// object, IMHO more elegant from a semantic point of view:
Pizza p = PizzaFactory().create().addAnchovies().addTomatoes().getPizza();

It allows for a quick grasp at what exactly is being created or how an object is manipulated, because the methods form one human-readable expression. It's definitely nice, but don't overuse. A rule of thumb is that this might be used with methods whose return value you could also declare as void.

Evaluating object properties

The second might be when a method evaluates something on an object. Consider, for example, the method car.getCurrentSpeed(), that could be interpreted as a message to an object asking for the current speed and returning that. It would simply return the value, not too complicated. :)

Make object do this or that

The third might be when a method makes an perform an operation, returning some sort of value indicating how well the caller's intention was fulfilled - but laying out such a method could be difficult:

int new_gear = 20;
if (car.gears.changeGear(new_gear)) // does that mean success or fail?

This is where you can see a difficulty in designing the method. Should it return 0 upon success or failure? How about -1 if the gear could not be set, because the car only has 5 gears? Does that mean the current gear is at -1 now, too? The method could return the gear it changed to, meaning you would have to compare the argument supplied to the method to the return code. That would work. On the other hand, you could simply return either true or false for failure or false or true for failure. Which one to use could be decided by estimating if you'd expect those method calls to rather fail or succeed.

In my humble opinion, there is a way to better express the semantics of such return values, by giving them a semantic description. Future developers interacting with your objects will love you for not having to look up the comments or documentation for your methods:

class GearSystem {
// (...)
public:
enum GearChangeResult
{ GearChangeSuccess, NonExistingGear, MechanicalGearProblem };

GearChangeResult changeGear (int gear);
};

That way, it becomes perfectly obvious for any programmer looking at your code, what the return value means (consider: if (gears.changeGear(20) == GearSystem::GearChangeSuccess) - much clearer what that means than the example above)

Antipattern: Failures as return codes.

The fourth possibility for a return value I actually omitted, because in my opinion it isn't any: when there's an error in your program, like a logic error or a failure that needs to be dealt with - you could theoretically return a value indicating so. But today, that's not done so often anymore (or should not be), because for that, there are exceptions.

mstrobl
Return codes for failures are not more used so often? Whole WindowsAPI is full of that. And even in .net theres plenty of this, just because exceptions take way to much processor cycles to be thrown. Look at the TryParse() Methods for example.
BeowulfOF
I try to make operational failures throw exceptions. Per the examples above, failures in the business logic can be described by returns. Programmatic errors (i.e. supplying a null object to a functor that would call methods on them) are to throw exceptions IMHO.
mstrobl
WRT speed of exceptions: ok, but then we'd have to remove many features of OOP as well, just to stay "fast". Oh and function calls add overhead too. :-) I work with enormous data sets, speed (or lack of) is in data structures and algorithms, not stack unwinding. CPU is cheap, manhours are not.
mstrobl
I find it specially interesting that the code you provide in your third block seems to fall directly in your fourth anti-pattern block. You are providing a result/failure as return value through GearChangeResult. How is that different from your anti-pattern?
David Rodríguez - dribeas
@dribeas: The method changeGears() is a message to the gears system to change to a particular gear - you are quite right that this is modelled incorrectly. It's not how a lever would work. But consider a gears system where you'd tell it a gear's number - any number could be supplied. (...)
mstrobl
(...) But failure to change to such an arbitrary gear would be something that affects the business model, or the behavior that is modelled by the program. It's not an error in the sense of flaw, but the result of modelled action. Those are my metaphors and work for me, of course, but ymmv. :)
mstrobl
Ah, another example that might clarify things: consider a 3D shooter. A user might continuously run against a wall. The result of the program's collision detection would be that there is a collision and the user can't walk further - like in the third block.
mstrobl
Then consider a list of walls with pointers to geometry supplied to a method that does intersection testing. The method would return indicating if there were any intersections. Now, if for example, one of the walls had a NULL pointer as geometry, that's a programmatic error, like in the antipattern.
mstrobl
A: 

Mostly the methods that logically don't return anything should be returning nothing - c++ "void". For languages that don't have such option, just don't bother to return anything. The failure should be indicated by an exception. "Search" methods are borderline - in those it's sometimes useful to return "special" value when what you were looking for is not found.

Smalltalk has another idiom that could be useful: "ifNotFound" value or block.

Arkadiy
+1  A: 

Returning this is also used in the "builder pattern", another case where method chaining can enhance readability as well as writing convenience.

A null is often returned as an out-of-band value to indicate that no result could be produced. I believe that this is perfectly reasonable when getting no result is a normal event; examples would include a null return from readLine() at end-of-file, or a null returned when providing a non-existent key to the get(...) method of a Map. Reading to the end of the file is normal behavior (as opposed to an IOException, which indicates that something went abnormally wrong while trying to read). Similarly, looking up a key and being told that it has no value is a normal case.

A good alternative to null for some cases is a "null object", which is a full-fledged instance of the result class, but which has appropriate state and behavior for a "nobody's home" case. For instance, the result of looking up a non-existent user ID might well be a NullUser object which has a zero-length name and no permissions to do anything in the system.

joel.neely