Over a million developers have joined DZone.

Design Choices: Return Values and Mocks

· Web Dev Zone

Make the transition to Node.js if you are a Java, PHP, Rails or .NET developer with these resources to help jumpstart your Node.js knowledge plus pick up some development tips.  Brought to you in partnership with IBM.

Let's suppose you have a graph of collaborating objects, such as this:

To perform some action, you have to send a message to the Facade object, that will collaborate with the rest of the graph to produce a result. There are at least two basic ways in which the output of this computation can exit this object graph:

  • through the return value of Facade.
  • By being sent to a Target object, passed in the initial call.

The second choice works like this:

facade.doSomething(Target target, ...)
// somewhere in the graph:

In this article I want to investigate which contexts call for the first solution, and which for the second one.


First of all we can say there is not much difference between the solution in some basic scenarios: it's just a matter of style. The first style reminds me of functional programming, while the second of the callback style of JavaScript applications, since its non-blocking nature makes the target objects and functions a necessity.

The cost of the solution in terms of code is similar. In the first case you have:

  • a return statement on the Facade
  • a return statement on the Intermediate objects
  • a return statement on the Leaf objects

In the second:

  • a Target interface
  • an additional parameter on the Facade
  • an additional parameter on the Intermediate objects
  • an additional parameter on the Leaf objects

Not that in some languages both the return statements and the interface construct may be implicit, so I'm not talking about lines of code and their length here; I'm counting the cost in time that we have to pay every time we read the code: a method with 3 parameters is relatively more difficult to understand with respect to the same method without one of them; in the same way, a void method is easier to reason about and modify than a method that returns a result.

Dimensions: who produces the result

The functional approach certainly wins when the final value cannot be directly produced by the Leaf objects.

For example, if you are representing a mathematical expression such as (1+2)*(3+4) with a Composite pattern, the value of the whole expression can only be computed in the Facade object. In this case, you only have the choice of putting a return statement there.

Consider instead the case where a Leaf has to be chosen, and is capable by itself to produce the result; for example, you're choosing which URL to redirect the user to, and each Leaf generates a different one while the rest of the graph chooses the Leaf to ask. Here passing a Target object to the Leaf is possible.

Dimensions: unit testing

In the second scenario above, unit testing of the Facade and the Intermediate object is influenced. In the case of return statements, we have a stub and an assertion:

Leaf leaf = mock(Leaf.class);
Intermediate intermediate = new IntermediateA(leaf);
assertEquals(23, intermediate.doSomething(input, ...);

Testing delegation is verbose (forgive my rusting Mockito skills). Consider instead using the Target object:

Target target = mock(Target.class);
Leaf leaf = mock(Leaf.class);
Intermediate intermediate = new IntermediateA(leaf);
intermediate.doSomething(input, ...);

and we can just test that target is passed down.

Dimensions: flexibility of the result

Moreover, this ease of unit testing persists even for maintenance, as you can change the Target interface without Facade and Intermediate objects having to know. You can refactor from:

interface Target {
  public void accept(String result);


interface Target {
  public void accept(String result, int anotherField);

by changing only the Leaf objects. The way you can achieve this in the return-based solution is by introducing an additional Value Object called Result, and wrap the String in there so that other fields can be added later without intervention on the return types of Facade and Intermediate.

Dimensions: producing a result at all

In some cases you may want to not produce a result. In the return-based solution, this requires a null value or a Null Object:

result = facade.doSomething(...);
if (result != null) {
  // use it

while this is easier with the Target object:

class Leaf1
  public void soSomething(Target target, ...)
  // call target.accept(), or do not call it

and this form also scales to multiple results of the same type:

public void doSomething(Target target, ...)


We have seen that different contexts call for different forms of the code to accomodate the requirements; moreover, changes we want to support are better dealt with one of the solutions with respect to the other. Design means searching for the appropriate form of your code, not sticking with the one you know better: as you see in the multiple results example, disruptive changes for one form are very easy to make in another design. Don't choose blindly...

Learn why developers are gravitating towards Node and its ability to retain and leverage the skills of JavaScript developers and the ability to deliver projects faster than other languages can.  Brought to you in partnership with IBM.


Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}