Showing posts with label error handling. Show all posts
Showing posts with label error handling. Show all posts

Thursday, October 4, 2012

Clean Code by Example: Throwing Exceptions in a Java Constructor

In the last post I weighted the utility of checked exceptions against unchecked exceptions. In this article I will investigate the perils of throwing exception from a constructor. As previously, I will try to stick to the guidelines given in Robert C. Martins «Clean Code». But since Joshua Bloch's «Effective Java» contributes much more to the topic of error handling than «Uncle Bob», it will be the authoritative source for this post. You can find the program code discussed here in this Github repository.

Exceptions thrown by a constructor

The architect reviewing my bowling example did not quiet like that my BowlingGame constructor threw an (checked) exception. My argument went, that it was there for mere convenience, to ease testing. With its help bowling games could be rolled wholesale, like so:

@Test
public void neitherStrikeNorSpare() {
  BowlingGame game = new BowlingGame(new int[] { 1, 2, 3, 4, 5, 1, 2, 3,
      4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 });
  assertEquals(60, game.getScore());
}

He countered, this could also be achieved by a normal method. And right he is! Additionally, with such a method, rolls could be added to a game in several bulks. So lets re-factor the constructor from:

public BowlingGame(int[] rolls) throws BowlingException {
  for (int roll : rolls) {
    addRoll(roll);
  }
}

into a «normal method» which the constructor calls:

public BowlingGame(int[] rolls) {
  addRolls(rolls);
}

public void addRolls(int[] rolls) {
  for (int roll : rolls) {
    addRoll(roll);
  }
}


Well, this some sleight of hand in that change. Where did the «throws BowlingException» declaration go? It already went during the last post in favor of an unchecked exception! The declaration is no longer required!

Notice something with the constructor? I did not, when I initially wrote this piece of code: the constructor called—and still calls—a non-final method! This is generally considered bad practice. If BowlingGame was sub-classed and addRoll/addRolls was overridden, then the constructor would call the sub-classes implementation although the sub-class constructor would have not been executed yet. That means the instance might not be in a usable state [Bloch, p. 89]! That makes one argument against using complex initialization within constructors: the temptation to call virtual methods.

As to further arguments against throwing exceptions in constructors—both Uncle Bob and Mr. Bloch give non. Most of the community on Stack Overflow seem to have no strong feelings against throwing exceptions from within constructors. Author and Stack Overflow veteran Jon Skeet, however, names two instances where one must be wary. The first is that the object under construction might escape immediate destruction and garbage collection:
It's possible for the «half-constructed» object to stick around though, if it's made itself visible earlier in the constructor (e.g. by assigning a static field, or adding itself to a collection).
—Jon Skeet, «Can constructors throw exceptions in Java?», Stack Overflow
The second pitfall is less exotic. It is related to resources that require clean-up:
Because the caller (usually) will have no way of using the new object, the constructor ought to be careful to avoid acquiring unmanaged resources (file handles etc) and then throwing an exception without releasing them. For example, if the constructor tries to open a FileInputStream and a FileOutputStream, and the first succeeds but the second fails, you should try to close the first stream. This becomes harder if it's a subclass constructor which throws the exception…
—Jon Skeet, «Can constructors throw exceptions in Java?», Stack Overflow
Both issues do not apply to the bowling constructor. The problem of calling a virtual method however must be fixed! There are various options: ditch the constructor altogether and call addRolls(int[]) instead on a freshly created instance. Alternatively, mark BowlingGame final thus preventing any sub-classes that could override addRolls(int[]). A little less drastic would be to just mark addRolls(int[]) and all the methods it calls recursively final`, which achieves the same effect. Or we can go for a static factory method:

public static BowlingGame fromRolls(int[] rolls) {
  BowlingGame game = new BowlingGame();
  game.addRolls(rolls);
  return game;
}

Apart from being permitted to call virtual methods, it also improves readability at the call site. In the following code the purpose of the int array is not immediately clear:

@Test
public void neitherStrikeNorSpare() {
  BowlingGame game = new BowlingGame(new int[] { 1, 2, 3, 4, 5, 1, 2, 3,
      4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 });
  assertEquals(60, game.getScore());
}

Replaced by a static factory method, it is clear:

@Test
public void neitherStrikeNorSpare() {
  BowlingGame game = BowlingGame.fromRolls(new int[] { 1, 2, 3, 4, 5, 1,
      2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 });
  assertEquals(60, game.getScore());

This is clearly the best way to go, in this case.
One questions remains: what about unit-tests for the newly created addRolls(int[]) method? I guess we are somewhat absolved from this obligation since the static factory method (and before that, the constructor) are already quiet thoroughly tested. If we opted to also test addRolls(int[]) the tests would be almost duplicated, with only a method call renamed. I do not know yet, if there is a pattern to efficiently write such test-cases.

Conclusion

There is nothing principally bad about throwing exceptions from a constructor. The danger of zombie objects or unreleased resources advise more against doing overly complicated stuff inside constructors as against throwing exceptions. So does the danger of accidentally calling virtual methods from the constructor that may easily break the object.

Tuesday, September 18, 2012

Ways of Signaling Errors by Return Value

This post summarizes the principal ways by which a method can signal its computation status (e.g. an error) to a caller—that is, by return value or by exception. Originally, I intended to do this as a short intermission in my upcoming post about re-factoring the exception handling of my bowling example to «Clean Code». Well, it grew and grew, so I decided to pull it out to keep the other article crisp.

There are basically three ways a method can signal an error—or any status—to its caller: by return value, by internal state change, or by method call. Returning a value is the most natural way, so I will focus on that. It comes in flavors. The most obvious is by means of the return statement in Java. Out-put parameters would be another way, but Java does not allow parameters to be passed out. Languages like C++ or PASCAL allow that through call-by-reference. In Java one would have to change the state of an input-parameter to archieve this effect. But that places the burden of managing this out-parameter on the caller. Hence, it is not really an option.

Yet another way for returning a value is—by throwing an exception. Exceptions try to solve two problems. The first is cramping computation result and computation status into a single return value. If a method is called merely for its side-effects, normal return values work fine. Instead of void, the methods is changed to yield a status value. The method «addRoll(int)» of the bowling example would change from:

public void addRoll(int roll) throws BowlingException {
  if (isComplete())

    throw new BowlingException("No more rolls allowed.");
  if (roll < 0 || roll > 10)
    throw new BowlingException("Roll out of bounds.");
  if (mExtraRolls > 0) {
    …
  }
}

to something like this:

public enum Status {
  GAME_COMPLETE,
  ROLL_OUT_OF_BOUNDS,
  OK
}

public Status addRoll(int roll) {
  if (isComplete())
    return Status.GAME_COMPLETE;
  if (roll < 0 || roll > 10)
    return Status.ROLL_OUT_OF_BOUNDS;
  if (mExtraRolls > 0) {
    …
  }
  return Status.OK;
}

While the exception is now gone, the code on the caller site is not going to look better. The try-catch-construct that mars the «main()»-method of the bowling example would have to be replaced by switch-case or if-then-else.

In cases where the method does not use all values of its co-domain–well, it is not swell but still viable to encode the return state into the unused values. In the bowling example, the method «getScore()»—if it would have to signal errors—could use any number of type int except for the range between 0 and 300. A negative number could signal an error, for instance. Constants have to be defined (and used!) for those error states or things might get out of hand. Readability of the code on the caller site suffers, however, by the additional state parsing.

With a bit-mask, both a value and a status can be encoded into a single integer. It is a quite common practice, especially in low level functions or on devices where memory is rare. Android does it in various places, for instance with «MotionEvents». As not only checking the state but also decoding the return value is required by the caller, readability suffers even more. Also debugging gets no easier. Defining special composite return types or using general collections less obfuscates the structure of the return value, but decoding and checking on the caller site will not go away.

As said before, exceptions address two problems. The first was coercing computation status and result into a single return value. This no longer happens, and decoding becomes unnecessary. The second is checking the state. It now happens in a code path altogether different from normal execution. Still readability is not great, because—as aspect-oriented programming would say–two concerns are intermingled that cannot clearly be expressed in the linear structure of source code.

Readability-wise it does not really matter whether to signal errors by return value or by exception. The caller has to treat the error-states separately from the normal return values and to act upon it. This might shroud the business logic of the caller. The best solution to this, it seems, is to avoid errors altogether! I will focus on this—among other things—in my next post.

Updates

09/18/2012: Use tags in references to the Github sources.