Bug Pattern In Java - The Impostor Type - Online Article

Overview

When special tags in fields are used to distinguish between types of objects, errors are possible in which a tag mislabels the associated data—a bug pattern known as the Impostor Type. We'll examine the symptoms and causes, define ways to prevent this error, and discuss some tempting hybrid implementations that don't use impostor types but hold danger nonetheless.

All but the most trivial of programs manipulate some types of data. Static type systems provide a way to ensure that a program doesn't manipulate data of a given type inappropriately.

One of the advantages of the Java language is that it is strongly typed, so that the possibility of a type error is eliminated before the program is ever run. As developers, we can use this type system to produce more robust and bug-free code. Often, though, the type system is not used to its full potential.

Many programs make less use of the static type system than they could, instead relying on special fields to contain tags that distinguish the types of data. By relying on these special fields, such programs give up the very protection that the type system was designed to provide. When one of these tags mislabels its data, it generates what I call an Impostor Type bug pattern.

In a nutshell, here is our latest bug pattern:

  • Pattern: Impostor Type.
  • Symptoms: A program that treats data of conceptually distinct types in the same way, or that doesn't recognize certain types of data.
  • Cause: The program uses fields with tags in lieu of separate classes for the various types of data.
  • Cures and Preventions: Divide conceptually distinct types of data into separate classes whenever possible. 
Note

The static type system is there to protect you from this type of bug. Use it.Weed bugs out during static checking.

About This Bug Pattern

This bug pattern results from using special fields inside classes to distinguish conceptually distinct subtypes. Because these subtypes are grouped together in the same class, static type checking is unable to catch a misuse of data that could have been caught if the subtypes were split into separate classes.

The Symptoms

One common symptom of an Impostor Type bug is that many conceptually distinct types of data are all treated in the same (and incorrect) manner when the program is run. Another common symptom is that data doesn't match any of the designated types.

As a rule of thumb, suspect this bug pattern whenever there is a mismatch between the conceptual type of data and the way it is handled by your program.

Note

Common symptoms include conceptually distinct types of data being treated in the same manner and data not matching any of the designated types.

To illustrate how easily bugs of this pattern can be introduced, let's consider a simple example. Suppose we want to manipulate various Euclidean forms, such as circles, squares, and so on. These forms will have no position, but they will have a scale, so that it will be possible to compute their area.

Implementation of Shapes with Impostor Types

public class Form
{
  String shape;
  double scale;
  public Form(String _shape, double _scale)
{
  this.shape = _shape;
  this.scale = _scale;
  }
  public double getArea()
{
  if (shape.equals("square"))
{
  return scale * scale;
  }
  else if (shape.equals("circle"))
{
  return Math.PI * scale * scale;
  130 
}
  else
{
// shape.equals("triangle"), an equilateral triangle 
return scale * (scale * Math.sqrt(3) / 4);
  }
}
}

There are serious disadvantages to implementing a data type in this way, even though you see it done often. One of the most glaring drawbacks is that this method is not very extensible. If we wanted to introduce a new shape for our forms (such as "pentagon"), we'd have to go in and modify the source code for the getArea() method. But extensibility is a separate concern; how does this programming style increase our susceptibility to errors?

Consider what would happen if, in some other part of the program, we constructed a new Form object as follows:

Constructing a New Form

The form "square" has been misspelled. But as far as the compiler is concerned, this is perfectly valid code.

Now consider what will happen when we try to call getArea() on our new Form object. Because the shape of the Form won't match any of the tests in the if- then-else block, its area will be computed in the else clause, as if it were a triangle!

There will be no error signaled. Indeed, in many circumstances, the return value will appear to be a perfectly reasonable number. Even if we put in some redundancy and check that the implied condition in the else clause holds (with an assertion, for instance), the error won't be found until the code is run.

Many other similar bugs might occur with the previous code. A clause might be accidentally left out of the if-then-else block, causing all Forms of the type corresponding to that clause to be handled improperly. Additionally, because the impostor type is just a String in a field, it might be modified, either accidentally or maliciously. Either way, such modifications could wreak all sorts of havoc.

The Cause

Again, the program uses fields with tags in lieu of separate classes for the various types of data.

Cures and Preventions

As you might have guessed, I suggest avoiding bugs of this type by using the type system to weed them out during static checking. Consider this alternative implementation:

Now consider what would happen if we were to mistype Square as Sqaure when creating a new Form. The compiler would signal an error, telling us that class Sqaure could not be found. The errant code would never even have a chance to run.

Similarly, the compiler would not allow us to forget to define getArea() for any of our subclasses. And, of course, it would be impossible for any object to change the type of a Form.

About the Author:

No further information.




Comments

No comment yet. Be the first to post a comment.