Core Java

OOP Alternative to Utility Classes

A utility class (aka helper class) is a “structure” that has only static methods and encapsulates no state. StringUtils, IOUtils, FileUtils from Apache Commons; Iterables and Iterators from Guava, and Files from JDK7 are perfect examples of utility classes.

This design idea is very popular in the Java world (as well as C#, Ruby, etc.) because utility classes provide common functionality used everywhere.

Here, we want to follow the DRY principle and avoid duplication. Therefore, we place common code blocks into utility classes and reuse them when necessary:
 

// This is a terrible design, don't reuse
public class NumberUtils {
  public static int max(int a, int b) {
    return a > b ? a : b;
  }
}

Indeed, this a very convenient technique!?

Utility Classes Are Evil

However, in an object-oriented world, utility classes are considered a very bad (some even may say “terrible”) practice.

There have been many discussions of this subject; to name a few: Are Helper Classes Evil? by Nick Malik, Why helper, singletons and utility classes are mostly bad by Simon Hart, Avoiding Utility Classes by Marshal Ward, Kill That Util Class! by Dhaval Dalal, Helper Classes Are A Code Smell by Rob Bagby.

Additionally, there are a few questions on StackExchange about utility classes: If a “Utilities” class is evil, where do I put my generic code?, Utility Classes are Evil.

A dry summary of all their arguments is that utility classes are not proper objects; therefore, they don’t fit into object-oriented world. They were inherited from procedural programming, mostly because most were used to a functional decomposition paradigm back then.

Assuming you agree with the arguments and want to stop using utility classes, I’ll show by example how these creatures can be replaced with proper objects.

Procedural Example

Say, for instance, you want to read a text file, split it into lines, trim every line and then save the results in another file. This is can be done with FileUtils from Apache Commons:

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");
}

The above code may look clean; however, this is procedural programming, not object-oriented. We are manipulating data (bytes and bits) and explicitly instructing the computer from where to retrieve them and then where to put them on every single line of code. We’re defining a procedure of execution.

Object-Oriented Alternative

In an object-oriented paradigm, we should instantiate and compose objects, thus letting them manage data when and how they desire. Instead of calling supplementary static functions, we should create objects that are capable of exposing the behaviour we are seeking:

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();

Potato, potato? Not really; just read on…

Objects Instead of Data Structures

This is how I would design the same file-transforming functionality as above but in an object-oriented 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);
}

FileLines implements Collection<String> and encapsulates all file reading and writing operations. An instance of FileLines behaves exactly as a collection of strings and hides all I/O operations. When we iterate it — a file is being read. When we addAll() to it — a file is being written.

Trimmed also implements Collection<String> and encapsulates a collection of strings (Decorator pattern). Every time the next line is retrieved, it gets trimmed.

All classes taking participation in the snippet are rather small: Trimmed, FileLines, and UnicodeFile. Each of them is responsible for its own single feature, thus following perfectly the single responsibility principle.

On our side, as users of the library, this may be not so important, but for their developers it is an imperative. It is much easier to develop, maintain and unit-test class FileLines rather than using a readLines() method in a 80+ methods and 3000 lines utility class FileUtils. Seriously, look at its source code.

An object-oriented approach enables lazy execution. The in file is not read until its data is required. If we fail to open out due to some I/O error, the first file won’t even be touched. The whole show starts only after we call addAll().

All lines in the second snippet, except the last one, instantiate and compose smaller objects into bigger ones. This object composition is rather cheap for the CPU since it doesn’t cause any data transformations.

Besides that, it is obvious that the second script runs in O(1) space, while the first one executes in O(n). This is the consequence of our procedural approach to data in the first script.

In an object-oriented world, there is no data; there are only objects and their behavior!

Related Posts

You may also find these posts interesting:

Reference: OOP Alternative to Utility Classes from our JCG partner Yegor Bugayenko at the About Programming blog.

Yegor Bugayenko

Yegor Bugayenko is an Oracle certified Java architect, CEO of Zerocracy, author of Elegant Objects book series about object-oriented programing, lead architect and founder of Cactoos, Takes, Rultor and Jcabi, and a big fan of test automation.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

33 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Jack
Jack
9 years ago

Hi,

Great post, thanks!

I think you touched some deeper concept here. The way you use objects, which is great, resembles functional programming. Composing objects is the same as composing functions with higher order functions. The data in your objects are equivalent to partial application of the function. Even lazy evaluation is exactly the trait from functional languages, e.g. Haskell.

Full respect.

Yegor Bugayenko
9 years ago
Reply to  Jack

Thanks! I believe that a true OOP application should look like a composition of a big amount of objects and a single method call at the end. Similar to what we have in LISP and other functional languages. The entire application is just a function that is called once.

Dejan Lekic
9 years ago

A really good programmer mixes paradigms – he or she is not slave to particular paradigm and tries to use the best approach to accomplish a task. Unfortunatelly both OOP and functional worlds are trying hard to stick to their paradigm, even when the approach simply do not make sense. I am Java developer myself and I am shocked sometimes when I see overengineered Java code that was made that way simply because author was trying hard to make things `trully OOP`.

Thank God Java 8 has lambda functions now, so Java programmers start slowly seeing benefits of functional programming.

Yegor Bugayenko
9 years ago
Reply to  Dejan Lekic

This is because Java was not designed as a pure object-oriented language. It is not suitable, as a language, for a pure OOP design. That’s why you are shocked sometimes, and I fully understand that. Java 8 with lambda functions is making the situation only worse. Instead of making Java more object-oriented Oracle is just following the market demand, trying to catch up with Ruby and many others, where a function is a first class citizen.

Yegor Bugayenko
9 years ago

Thanks for your comment! Subscribe to my blog, I write a lot about OOP design: http://www.yegor256.com/about-me.html

PhiLho
9 years ago

Uh? Maybe the file example is relevant, but the math example is just plain stupid. Even if JVM became better at handling short-lived small objects with escape analysis (is it always activated, now?), I would protest if you created an object just to compute the max of two numbers! Not to mention performance cost, creating objects, assigning values to fields has probably a high cost compared to a simple function call. (And I abhor the habit of systematically prefix fields with this., but that’s a stylistic point, irrelevant in this discussion.) Having static functions isn’t necessarily procedural (like in C… Read more »

Adam Beneschan
Adam Beneschan
9 years ago
Reply to  PhiLho

“Uh” was about the reaction I had to the Max example also. For one thing, Number is an abstract class, not an interface, so “implements Number” would be rejected straight away. For another thing, even if it said “extends Number”, the Max class would have to override longValue(), floatValue(), etc., which it doesn’t. But the biggest objection is that a “Max” isn’t a number. The other classes that extend Number are Integer, Long, Double, etc., all classes that represent a number. I could imagine implementing a RationalNumber class that extends Number, because an instance of that class does represent a… Read more »

Adam Beneschan
Adam Beneschan
9 years ago
Reply to  PhiLho

One more thing: “The file example might be better, although I disagree with the big-O conclusion: whatever the method, the time will be proportional to the number of lines in the file.” The author was referring to space, not time. The procedural method reads all the lines into memory first. The second example presumably does not, although we weren’t shown all the code. However, the author is way off base with his statement: “Besides that, it is obvious that the second script runs in O(1) space, while the first one executes in O(n). This is the consequence of our procedural… Read more »

Lazycoder
Lazycoder
9 years ago

There’s one huge problem with this approach – overhead when creating object.

I would say that combining the two approaches, specially when there are lambdas and other stuff is much more wise than blindly build hundreds of objects for no reason.

Assuming that guys who wrote guava and apache commons libs did not know their shit is a huge mistake.

Yegor Bugayenko
9 years ago
Reply to  Lazycoder

Java itself is our enemy. It is not designed to be pure object-oriented, and it doesn’t help us when we want to make our software in a true OOP spirit. “Overhead when creating object” is what you know about JVM. And this is what influences your design decisions. But it shouldn’t! The platform should help you to design right. Unfortunately, it doesn’t. But I hope that eventually Java will improve or we will get a better language/platform where creating an object will be as fast as calling a function.

James
James
9 years ago

I’m sorry, but if you think that knowledge of the JVM shouldn’t influence your design decisions then you’re not living in the real world. I’m paid to make software that works and is maintainable, not to follow theoretical ideals.

Java isn’t our enemy, it’s one of our tools. If you know how to use it, it can be a good tool. Like any tool, if you don’t understand (or choose to ignore) how it works you won’t get the most out of it.

PhiLho
9 years ago

BTW, thanks for the comprehensive list of links a the start of the article. Funnily, most of them are more nuanced than your article… I agree more with most of them: utility classes might be a code smell, and a programmer should double-check before making one. Some solutions given in the links are irrelevant for Java, like C#’s extension, Groovy’s delegate, etc. So, in Java, sometime we have no choice, as said in my previous message: a single, short function doing only one thing without dependency on tons of other utility functions, and not having a ton of parameters, has… Read more »

Yegor Bugayenko
9 years ago
Reply to  PhiLho

(I can’t moderate anything here, it’s up to JCG moderators)

“In Java, sometime we have no choice” — I don’t get it. Why and where we have no choice? Because Oracle/Sun gives us improperly designed Math class and we should use it? Because Apache created that StringUtils, IOUtils, you-name-it-Utils, and we don’t have any better alternative? I believe in better future of OOP :)

freeman
freeman
9 years ago

First of all I want to say that your example is rather idealistic than realistic. I mean the second one. The first one is nice example of where utility class SHOULD be used. Don’t mess up newbies that java.lang.Math is poorly written/design. It does exactly what it should in the way it should. And you should strongly reccomend its ussage. It is all about performance and easeniest of using it. I always says: “adjust design to the project, not project to the design/ideaology”. OOP and TDD fanatics might not agrre with this, I know… 1. Creating a new object each… Read more »

freeman
freeman
9 years ago

First of all I want to say that your example is rather idealistic than realistic. I mean the second one. The first one is nice example of where utility class SHOULD be used. Don’t mess up newbies that java.lang.Math is poorly written/design. It does exactly what it should in the way it should. And you should strongly recommend its usage. It is all about performance and ease of usage. I always says: “adjust design to the project, not project to the design/ideaology”. OOP and TDD fanatics might not agree with this, I know… 1. Creating a new object each time… Read more »

James
James
9 years ago

You really think:

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

… is better than…

int max = max(10,5);

?

I’m not saying you’re wrong in many other cases, but IMO you picked an example where the utility class seems fully justified. It makes the code simpler and more readable, mocking isn’t a concern, and surely you’ll never want to provide an alternative implementation?!

In my time I’ve come across so much overly complicated code written by dogmatic developers attempting to follow principles that shouldn’t have been applied in that particular case. To me KISS should be the overriding principle here.

Yegor Bugayenko
9 years ago
Reply to  James

Yes, I really think that new Max(10,5).intValue() is much better than max(10,5). Also, I think that object-oriented programming is better than procedural one. I also think that in an object-oriented language (like Java) we should write OOP, not COBOL. max(10,5) is a call of a procedure. This method exists outside of an object, which is against the entire idea of OOP. Besides theoretical inconsistency (which makes you puke when you see it, if you truly understand OOP), there are two practical problems – composability and testability. You can’t compose max(10,5) into another object. You can’t give it as a parameter… Read more »

James
James
9 years ago

What is not testable about max(10, 5)?

You seem to see OOP as a holy grail that should be applied in all cases, irrespective of benefit. I simply don’t understand this standpoint. I always ask myself what is the benefit of what I’m doing. This has nothing to do with me not understanding OOP, it’s about giving my clients what they are paying me for rather than using their money to blindly follow technical ideologies.

Yegor Bugayenko
9 years ago
Reply to  James

You can’t mock max(10,5). It’s a hard-coded dependency. In case of Math.max() this may not be a big issue, because we believe that this static function always works. But if it doesn’t, you end up with debugging, instead of unit testing. I see OOP as a philosophy, a religion, if you wish :) Either 1) you follow it, write beautiful code and enjoy programming (not just the money you get for it), or 2) you violate its fundamental principles, write something average and hate to see your code after it’s done (and paid by someone, of course). I choose the… Read more »

James
James
9 years ago

If OOP is a religion, then you’re a fundamentalist. You see this as black or white. You think that it’s either 100% OOP, or it’s wrong.

I don’t apply this thinking to work, religion, philosophy or anything else in life. Your choice is a fallacy because those are not the only 2 options. Guess what… I can violate OOPs principles and still write clean code. But you don’t see it, because you’re locked in to a purist mindset.

James
James
9 years ago

Also, you previously said max(10,5) wasn’t *testable*. That’s completely different from mockable. Hopefully you know the difference.

Stephen Dycus
Stephen Dycus
9 years ago

Java has *lambda* expressions, an almost entirely functional paradigm concept, and you’re clinging on to some strange assumption that Java is a pure OOP environment? It just sounds like you’re unable to see OOP as anything other than an all encompassing solution to every programming problem in Java. Are the numerous individuals who developed java’s features and apis are wrong? I’m guessing with your mention of testability that you’re also one of those guys that takes unit testing *way* too far. Don’t get me wrong, unit testing is great in the right environment (I write them for my company’s service… Read more »

S Williams
S Williams
9 years ago

In Item 4 of Effective Java, 2nd edition, Joshua Bloch writes that classes that are just a grouping of static methods “… do have valid uses. They can be used to group related methods on primitive values or arrays, in the manner of java.lang.Math or java.util.Arrays.”

Are you saying that Bloch is wrong?

Yegor Bugayenko
9 years ago
Reply to  S Williams

Yes, I’m saying that Joshua Bloch is very wrong.

James
James
9 years ago

You should let him know so that he can kneel down before you and repent his foolishness.

Serhij Pylypenko
Serhij Pylypenko
9 years ago
Reply to  James

“… do have valid uses. They can be used to group related methods on primitive values or arrays, in the manner of java.lang.Math or java.util.Arrays.”

Please, try to be attentive and careful.
Bloch talks about primitives and arrays, that are exclusions from OO theory.
Therefore, ‘helper class’ just accommodates those exclusions within OO programming language.
If primitives and arrays were banished, ‘helper classes’ may be discarded as well.

I can’t see why Bloch is wrong.

Jerry
Jerry
9 years ago

Goals of software development:

1. meet specs within deadlines.
2. write code that’s as easy as possible to understand and therefore maintain and modify.

Designing a system, a real world system, using nothing but purified OOP… Ack! Have you taken any looks at Swing code? Beautiful looking – but difficult to understand, a nightmare to maintain or add new features.

Oliver Doepner
9 years ago

Regarding the Max() example: I don’t think a Max class makes any sense. As others have pointed out its instances would be throw-away objects that are not actually a subtype of Number. A more appropriate OO approach to calculating maximums might be a type (interface) Order<T extends Comparable> with methods like T max(T… values); T min(T… values); T[] sort(T… values); etc., probably the same methods with parameter Iterable also make sense. The Order interface could be implemented by a default implementation OrderImpl using the natural ordering, since T would be a subtype of Comparable. A class that has to perform… Read more »

Niki
Niki
9 years ago

Wow.. The same discussion copy/pasted from LinkedIn.. So having a static constructor is OK, which is also something existing outside the Class is fine, but using static method is not. If only some one listed the principle of the OOP that helper classes broke This is a ridiculous discussion leading to nothing but flames. I myself personally will never ever allow making classes and creating instances of them just to be OOP compliant in anything under my management. Yegor.. your opinion is that Bloch is wrong..I would say that he is right and you are wrong. Utility classes DO NOT… Read more »

greg
greg
9 years ago

Dude. I just read your article on string concatenation. And in it you have no problem stating you prefer to use StringUtils over StringBuilder.
So even you can see that helper classes are sometimes better in certain situations.

Madhavi
Madhavi
8 years ago

Nice post and great insights by the knowledgeable people.

Back to top button