Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

No return statements

DZone's Guide to

No return statements

· Web Dev Zone
Free Resource

Add user login and MFA to your next project in minutes. Create a free Okta developer account, drop in one of our SDKs to your application and get back to building.

One of the variations we always propose in our local edition of the Code Retreat is forbidding the use of return statements (or of method whose type is not void in languages where return isn't present). The goal the inventor of this variant had in mind was to stimulate the development on an extreme Tell Don't Ask solution where there are no getters and all state is shared by sending messages between objects.

This is a noble goal, since we are usually taught the Ask interactions by our universities and IDEs who generate getters; even if an entire solution based on Tell only is impractical, it can restore the balance so that we develop two different techniques instead of just one to use in real code.

Needless to say, it's not easy to just Tell, so I have collected here a few Tell Don't Ask patterns to get you started if you have never tried to implement the Game of Life (or any other kata) this way. Note that these examples, to avoid being difficult to understand, will use Tell Don't Ask only for a specific interaction; other objects around them will continue using their standard protocols.

Callbacks

One of the simplest way to make information flow from a a callee back to its caller its to inject an anonymous function, usually going by the name callback. Javascript code, being based on asynchronous IO, is ripe with this pattern:

$.get(url, function(text) {
  $('div.box').value(text);
});

The caller contains this code; the callee, $.get, will call the passed function when a result will be available (the exact concurrency model is out of the scope of this article.)

Another simple example in the callback field is the Iterator object.

foreach ($iterator as $element) {
  print($element);
}

becomes

iterator.each(function($element) {
  print($element);
});

Command object

It's possible to establish an explicit type or protocol for the callback, especially in statically typed languages.

public void get(String url, Canvas element) {
  // suppose text is calculated here somewhere
  element.value(text);
}
connection.get(url, document.findByCssSelector('div.box'));

Passing this

Sometimes the Command object is just a role that the client object assume:

public void get(String url, Canvas Element) {
  // suppose text is calculated here somewhere
  element.value(text);
}

class AjaxTextBox implements Canvas
{
  // the constructor is easy to guess

  public void populate(Connection connection)
  {
    connection.get(this.url, this);
  }
}

Visitor-like

The previous patterns only invert a relationship between two objects. However, we can use the same reasoning to collect data from multiple objects in a way similar to the Visitor pattern:

class Condition
{
  private boolean value = true;

  public Condition(boolean value)
  {
    this.value = value;
  }

  public void tellTo(CompositeCondition condition)
  {
    if (this.value) {
      condition.addTrue();
    } else {
      condition.addFalse();
    }
  }
}

class AndCondition implements CompositeCondition
{

  private boolean value = true;

  public void addFalse()
  {
     this.value = false;
  }

  public void addTrue()
  {
  }
}

condition = new AndCondition();
(new Condition(a == b)).tellTo(condition);
(new Condition(c > d)).tellTo(condition);

Notice how dependencies are inverted: concrete Condition classes depends on CompositeCondition instead of the other way around. There is a lot to explore here.

Of course every Condition would have to implement these methods:

public void ifTrue(callback)
{
  if (value) {
    callback();
  }
}

public void ifFalse(callback)
{
  if (!value) {
    callback();
  }
}

which is how Smalltalk implemented booleans with objects. With time, abstractions may emerge from the ifTrue and ifFalse jungle:

class Neighborhood
{
  public void crowded?(callback)
  {
    if (aliveNeighbors > 3) {
      callback();
    }
  }

  public void solitude?(callback)
  {
    if (aliveNeighbors < 1) {
      callback();
    }
  }
}

and this is the only GOL-related code I'll include here.

Conclusions

In the cycle of mechanical and intuitive steps of Agile design, this exercise starts with mechanical and forces you to implement some of these patterns (or other ones you know) to avoid getters. The particular shape of the code then can make some new abstractions emerge, with an intuitive step: behavior is cut into different pieces and you can find new names that were not applicable before; the concepts used to represent the problem may be different from the Cell and Board we rely on. This is why constraints are valuable: to force different design directions that let us learn something new.

Launch your application faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}