Helper class is evil

Share

Utility classes (or helper classes), “structures” that have only static methods, which as design is very popular in Java world (as well as in C#, Ruby, Python worlds) as they can provide usual functionalities that are used everywhere in a simple manner.

If we want to follow DRY principal, and most of us consider it to be obvious way to go, and if we are avoiding code duplication, we put common code blocks in utility classes and use them again when needed:

// THIS IS TERRIBLE, don’t reuse
public class NumberUtils {
  public static int max(int a, int b) {
    return a > b ? a : b;
  }
}

DRY usually stands for “Don’t repeat yourrself”, and main usage of this principal is to reduce information repetition.

“Every information or piece of data have to have unique, ambiguous, authoritative representation inside the system.” So this really looks like very convenient technique, isn’t it?! And what’s more, applicable to wide spectrum of things, including DB schemas, test plans, system builds and even documentation.

But, in object-oriented world, helper classes are considered to be very bad (some would even say terrible) practice. And there are a lot of discussions on this subject. So question is obvious:

If helper classes are bad, where should I put my generic code?

Well, utility classes are in fact bad. And conclusion of all discussion is that utility classes are not real objects, and as such they don’t have a place in Object Oriented world. They are part of inherited procedural programming, mostly because we are used to paradigm of functional decomposition in earlier periods and we tend to stick to that.

Assuming that you agree with me, and that you want to stop with using helper classes, here is the simple example how those abominations can be substituted with real Objects:

Say we want to read text file, split it into lines, trim every line and then save results in another file. This can be done with ApacheCommons FileUtils:

void transform(File in, File out) {
  Collection<String> src = FileUtils.readLines(in, "UTF-8");
  Collection<String> dest = new ArrayList<>(src.size());
  for (String line : src) {
    dest.add(line.trim());
  }
  FileUtils.writeLines(out, dest, "UTF-8");
}

This snippet may look as clean code, but this is in fact procedural programming and not object oriented. We are manipulating data and explicitly instruct computer where to pull it from and to put it with every line of code. We define execution procedures.

In object oriented paradigm, we should instance and compose objects and allow them to manipulate data in a way they want to. Instead of calling supplementary static functions, we should create objects capable of expressing behavior we desire from them:

public class Max implements Number {
  private final int a;
  private final int b;
  public Max(int x, int y) {
    this.a = x;
    this.b = y;
  }
  @Override
  public int intValue() {
    return this.a > this.b ? this.a : this.b;
  }
}

This procedural call:

int max = NumberUtils.max(10, 5);

Will become object oriented:

int max = new Max(10, 5).intValue();

Well it’s the same as before…? Let’s continue…

How can we make same file-transforming functionality as before but this time in OO manner?

void transform(File in, File out) {
  Collection<String> src = new Trimmed(
    new FileLines(new UnicodeFile(in))
  );
  Collection<String> dest = new FileLines(
    new UnicodeFile(out)
  );
  dest.addAll(src);
}

Now FileLines implements Collection<String> and encapsulates all file read and write operations. Instance of FileLines behaves as string collection and hides all I/O operations under the hood. When iterated – file is being read. When we put addAll() to that – file is being written.

Trimmed also implements Collection<String> and encapsulates string collection (following Decorator pattern). Each time line is pulled, it automatically gets trimmed.

All classes involved in this code piece are tiny: Trimmed, FileLines and UnicodeFiles. Each of them is responsible for its own feature so that they can perfectly follow Single Responsibility principal.

From library user side, this might not seem that important, but for developers this is imperative. It’s much easier to make, maintain and cover with unit tests class such as FileLines than use readLines() in 80+ methods and 3000 lines of helper class FileUtils.

Object oriented approach enables lazy execution. Input file is not being read until its data is necessary. If we fail to access file due to error, file is not going to be touched, and data will remain consistent. Show begins only after we call addAll().

All lines but last in 2nd code piece, are instanced and connect smaller objects into bigger ones. Object composing does not waste CPU resources as it’s not demanding data transformation. Apart from that, it’s obvious that 2nd script uses O(1) space while 1st uses O(n). That’s the consequence of procedural approach in 1st one.

There is no data in object oriented world. There are just objects and their behavior!

Share

Sign in to get new blogs and news first:

Leave a Reply

Strahinja Dević

Senior Developer in Test@Endava
mm

Graduated and completed Master studies at Grenoble University in France (Joseph Fourier, Class 2005). Has 8+ years in penetration testing, framework development and overall testing practice.

At the moment, working for Endava Belgrade as Senior Developer in Test, but also has previous experience on Enterprise projects, such as Kroll Ontrack, Invensys, Compucon, Midway.

Has extensive experience with leading Pentest teams, in order to answer the threats against systems complacent with PCI standards as well as RedTeam testing, full scale automation solutions, on click deployment etc.

Sign in to get new blogs and news first.

Categories