Are utility classes evil? [closed]
Asked Answered
C

14

120

I saw this question: If a "Utilities" class is evil, where do I put my generic code?

And I thought, why are utility classes evil?

Let’s say I have a domain model that is dozens of classes deep. I need to be able to xml-ify instances. Do I make a toXml method on the parent? Do I make a MyDomainXmlUtility.toXml helper class? This is a case where the business need spans the entire domain model -- does it really belong as an instance method? What about if there are a bunch of auxiliary methods on the XML functionality of the application?

Cicatrix answered 27/7, 2010 at 0:55 Comment(10)
The devaluation of the term "evil" is evil!Shaikh
@matthew i preserved the terms of the post on which my question is based...;)Cicatrix
Utility classes are a bad idea for the same reasons singletons are.Muster
The debate on whether to have a toXML method is one that is centered on rich versus anemic domain models. codeflow.blogspot.com/2007/05/anemic-vs-rich-domain-models.htmlTruthvalue
@james, the toXML is just an example...what about some regex functionality that is used all over the place? Like, you need to do some stuff with the strings across your domain model, but you couldnt use subclassing due to another overriding concern that used your one superclass (in java)Cicatrix
I get you. In Java with the anemic approach the toXML method would be placed in a Services class (manipulates DO but not related to the state of one single DO). The regex would indeed end up in a utilities class. I don't see what the alternative to that would be.Truthvalue
@james, but if it had to be used across the domain, which one service would handle it? I suppose and XmlService, but then whats the difference from the static utility, other than one is static and one is not?Cicatrix
Good question. Usually a service would correspond one-to-one with one of the domain model classes (there can be exceptions). For example, there'd be a UserService class with a toXML method that accepts User instances and processes it's attributes. This in turn would call up an XMLUtilities class that would actually transform the data into XML. The main difference between utilities and services is that the latter are instantiated. The reason is that more than one service of the same type might be needed. This has more to do with a given convention in architecture than design/OOP.Truthvalue
@james, right, but the underlying issue that if the xml concern was truly cross domain, you would might need a utility rather than service to avoid duplicating code, assuming the services were sufficiently complex and no common functionality could be put in a super class. This is mental masturbation at this pointCicatrix
I understand. This approach is just adding a layer and the duplicate code ends up in a utility class anyway. I also agree with your conclusion. It's a pain advancing in a language beyond a certain level because conventions aren't always justified. In the end, choices should be made according to the high-cohesion/low-coupling ideal.Truthvalue
L
149

Utility classes aren't exactly evil, but they can violate the principles that compose a good object-oriented design. In a good object-oriented design, most classes should represent a single thing and all of its attributes and operations. If you are operating on a thing, that method should probably be a member of that thing.

However, there are times when you can use utility classes to group a number of methods together — an example being the java.util.Collections class which provides a number of utilities that can be used on any Java Collection. These aren't specific to one particular type of Collection, but instead implement algorithms that can be used on any Collection.

Really, what you need to do is think about your design and determine where it makes the most sense to put the methods. Usually, it's as operations inside of a class. However, sometimes, it is indeed as a utility class. When you do use a utility class, however, don't just throw random methods into it, instead, organize the methods by purpose and functionality.

Lashanda answered 27/7, 2010 at 1:1 Comment(4)
If the language doesn't offer any namespace mechanism other than classes, you have no choice but to abuse a class where a namespace would do. In C++, you can put a freestanding function, unrelated to other functions, into a namespace. In Java, you must put it into a class as a static (class) member.Tedda
Grouping as methods may be canonic from an OOP standpoint. However, OOP is rarely the solution (among the exceptions are high-level architecture and widget toolkits as one of the instances where the incredibly broken semantics of code reuse by inheritance actually fit) and generally methods increase coupling and dependencies. Trivial example:if you provide printer/scanner methods to your class as methods, you couple the class to printer/scanner libraries. Plus, you fix the number of possible implementations to effectively one. Unless you throw in interface and further increase dependencies.Neume
On the other hand, I agree with your sentiment "group by purpose and functionality". Let's revisit the printer/scanner example again, and start with a set of concerting classes, design for a common purpose. You may want to write some debug code and design a textual representation for your classes. You can implement your debug printers in a single file which depends on all those classes and a printer library. Non-debugging code won't be burdened with the dependencies of this implementation.Neume
Util and Helper classes are an unfortunate artifact of such constraints, and those who don't understand OOP or don't understand the problem domain have continued to write procedural code.Caryl
T
69

I think that the general consensus is that utility classes are not evil per se. You just need to use them judiciously:

  • Design the static utility methods to be general and reusable. Make sure that they are stateless; i.e. no static variables.

  • If you have lots of utility methods, partition them into classes in a way that will make it easy for developers to find them.

  • Don't use utility classes where static or instance methods in a domain class would be a better solution. For example, consider if methods in an abstract base class or an instantiable helper class would be a better solution.

  • For Java 8 onwards, "default methods" in an interface may be a better option than utility classes. (See When to use: Java 8+ interface default method, vs. abstract method for example.)


The other way to look at this Question is to observe that in the quoted Question, "If utility classes are "evil"" is a strawman argument. Its like me asking:

"If pigs can fly, should I carry an umbrella?".

In the above question I am not actually saying that pigs can fly ... or that I agree with the proposition that they could fly1.

Typical "xyz is evil" statements are rhetorical devices that are intended to make you think by posing an extreme viewpoint. They are rarely (if ever) intended as statements of literal fact.


1 - And you should NOT interpret that strawman question as advice on whether you should always take an umbrella with you when you are outdoors.

Tracheostomy answered 27/7, 2010 at 0:56 Comment(0)
H
19

Utility classes are problematic because they fail to group responsibilities with the data that supports them.

They are however extremely useful and I build them all the time as either permanent structures or as stepping stones during a more thorough refactor.

From a Clean Code perspective utility classes violate the Single Responsibility and the Open-Closed Principle. They have lots of reasons to change and are by design not extensible. They really should only exist during refactoring as intermediate cruft.

Hanahanae answered 27/7, 2010 at 0:59 Comment(11)
i thought utility classes were built to support data, not the other way around, like you intoned...?Cicatrix
To clarify my answer, the role of a class is to provide instance objects that combine state with a series of methods that act on that state. My definition of a Utility Class for which no instances can be created, has no state of its own and only stateless static methods that act purely on their input. This makes them oddballs in an object oriented system since they are not really classes at all. However, that is largely a philosophical problem rather than a practical problem.Cariole
I would call it a practical problem since e.g. in Java you cannot create any functions that are not methods in a class. In such a language the "class with no variables and with only static methods" is an idiom that stands for a namespace of utility functions... When faced with such a class in Java, it is IMHO invalid and pointless to speak of a class, even though the class keyword is used. It quacks like a namespace, it walks like a namespace - it is one...Tedda
@KubaOber interesting point. I think that a full object-oriented decomposition of a system tends not to have a home for pure namespaces like that. I have found them very limiting compared to using interfaces and dependency injection. Lambda expressions and method references may give them a rennaissance however since they can be automatically made to conform to an equivalent signature in a functional interface.Cariole
I'm sorry to be blunt, but "stateless static methods [...] oddballs in an OOP system [...] philosophical problem" is extremely misguided. It's GREAT if you can avoid state because that makes it easy to write correct code. It's BROKEN when I have to write x.equals(y) because 1) the fact that this really shouldn't modify any state whatsoever isn't conveyed (and I can't rely on it not knowing the implementation) 2) I never intended to put x in a "favored" or "acted upon" position compared to y 3) I don't want to consider "identities" of x or y but am merely interested in their values.Neume
Really most functionality in the real world is best expressed as static, stateless, mathematical functions. Is Math.PI an object that should be mutated? Do I really have to instantiate an AbstractSineCalculator which implements IAbstractRealOperation just to get the sine of a number?Neume
@JoSo I completely agree on the value of statelessness and direct computation. Naive OO is philosophically opposed to this and I think that makes it deeply flawed. It encourages abstraction of things that don't need it (as you have demonstrated) and fails to provide meaningful abstractions for actual computation. However, a balanced approach to using an OO language tends toward immutability and statelessness as they promote modularity and maintainability.Cariole
@AlainO'Dea: I realize I misread your comment as standing against stateless functionality. I apologize for my tone - I'm currently involved in my first Java project (after I avoided OOP for the most part of my 10 years of programming) and am buried under layers of abstract things with unclear state and meanings. And I just need some fresh air :-).Neume
Btw. the "data hiding" aspect of OOP is of course the right approach for modularity between services at the very highest levels of an architecture. However, at the lower levels you just need to get dirty, and abstraction does actually introduce complexity: It hinders efficient implementation and increases dependencies and coupling (for a cynic example, introducing and depending on IAbstractInt makes my code much less portable than just using the int that is behind it anyway)Neume
@JoSo there's an interesting thing we've discovered in making our system at work distributed and more modular: data representations (aka naked structs with no behavior) are the best way to integrate modules to limit coupling. It maps nicely on to messaging yet still works well within a process. The AWS Java SDK follows this approach as well with request objects separated from APIs that consume them.Cariole
@AlainO'Dea: Happy to hear that. I followed that approach as well making a graph representation for abstract automata. Really great for serialization. It's sad that Java has no real POD structs but only objects. Couldn't get rid and allocation overhead when all i wanted is to represent edges (just looking at the graph as a relation, not giving identity to nodes or edges). std::vector is one of only few good arguments for C++ there. This site may be of interest to you (or it may not - I don't have much experience with business software): dataorienteddesign.com/dodmainNeume
T
10

I suppose it starts to become evil when

1) It gets too big (just group them into meaningful categories in this case).
2) Methods that should not be static methods are present

But as long as these conditions are not met, I think they are very useful.

Tafia answered 27/7, 2010 at 1:2 Comment(2)
"Methods that should not be static methods are present." How is this possible?Dermatoplasty
@KorayTugay Consider the non-static Widget.compareTo(Widget widget) versus the static WidgetUtils.compare(Widget widgetOne, Widget widgetTwo). Comparison is an example of something that shouldn't be done statically.Argumentation
D
8

Rule of thumb

You can look at this problem from two perspectives:

  • Overall *Util methods are often a suggestion of bad code design or lazy naming convention.
  • It is legitimate design solution for reusable cross-domain stateless functionalities. Please note that for almost all common problems there are existing solutions.

Example 1. Correct usage of util classes/modules. External library example

Let's assume you are writing application which manages loans and credit cards. Data from these modules is exposed through web services in json format. In theory you can manually convert objects to strings which will be in json, but that would reinvent the wheel. Correct solution would be to include in both modules external library used to convert java objects to desired format. (in example image I have shown gson)

enter image description here


Example 2. Correct usage of util classes/modules. Writing your own util without excuses to other team members

As a use case assume that we need to perform some calculations in two modules of application, but both of them need to know when there are public holidays in Poland. In theory you can make these calculations inside modules, but it would be better to extract this functionality to separate module.

Here is small, but important detail. Class/module you have written is not called HolidayUtil, but PolishHolidayCalculator. Functionally it is a util class, but we have managed to avoid generic word.

enter image description here

Dzoba answered 5/6, 2017 at 10:0 Comment(1)
A yEd fan I see ;) Amazing app.Sorcerer
B
5

Utility classes are bad because they mean you were too lazy to think up a better name for the class :)

That being said, I am lazy. Sometimes you just need to get the job done and your mind's a blank .. that's when "Utility" classes start creeping in.

Baudin answered 27/7, 2010 at 1:41 Comment(0)
O
4

I don't entirely agree that utility classes are evil.

While a utility class may violate OO principles in some ways, they aren't always bad.

For example, imagine you want a function that will clean a string of all substrings matching the value x.

STL C++ (as of now) doesn't directly support this.

You could create a polymorphic extension of std::string.

But the problem is, do you really want EVERY string you use in your project to be your extended string class?

There are times when OO doesn't really make sense, and this is one of them. We want our program to be compatible with other programs, so we will stick with std::string and create a class StringUtil_ (or something).

I'd say it's best if you stick with one util per class. I'd say it's silly to have one util for all classes or many utils for one class.

Oversoul answered 9/6, 2016 at 13:15 Comment(0)
O
3

Looking back at this question now, I'd say that C# extension methods completely destroy the need for utility classes. But not all languages have such an in-genius construct.

You also have JavaScript, where you can just add a new function right to the existing object.

But I'm not sure there really is an elegant way to solve this problem in an older language like C++...

Good OO code is kinda hard to write, and is hard to find since writing Good OO requires a lot more time/knowledge than writing decent functional code.

And when you're on a budget, your boss isn't always happy to see you've spent the whole day writing a bunch of classes...

Oversoul answered 21/7, 2017 at 21:19 Comment(0)
C
2

It's very easy to brand something a utility simply because the designer couldn't think of an appropriate place to put the code. There are often few true "utilities".

As a rule of thumb, I usually keep code in the package where it is first used, and then only refactor to a more generic place if I find that later it really is needed elsewhere. The only exception is if I already have a package that performs similar/related functionality, and the code best fits there.

Covenantor answered 27/7, 2010 at 1:0 Comment(0)
S
2

Utility classes containing stateless static methods can be useful. These are often very easy to unit test.

Seraphic answered 27/7, 2010 at 3:44 Comment(0)
S
2

Most Util classes are bad because:

  1. They broaden the scope of methods. They make code public that would otherwise be private. If the util method is needed by multiple callers in separate classes and is stable (i.e. doesn't need updating) it's better in my opinion to copy and paste private helper methods into the calling class. Once you expose it as an API, you make it harder to understand what the public entry point to a jar component is (you maintain a tree structured called hierarchy with one parent per method. This is easier to mentally segregate into components which is harder when you have methods called from multiple parent methods).
  2. They result in dead code. Util methods over time become unused as your app evolves and you end up with unused code polluting your code base. If it had remained private your compiler would tell you the method is unused and you could just remove it (the best code is no code at all). Once you make such a method non private your computer will be powerless to help you remove unused code. It may be called from a different jar file for all the computer knows.

There are some analogies to static vs dynamic libraries.

Sorcerer answered 31/5, 2016 at 18:20 Comment(0)
C
1

With Java 8 you can use static methods in interfaces ... problem solved.

Cairngorm answered 12/4, 2016 at 12:20 Comment(2)
It doesn't address the problems stated in https://mcmap.net/q/181522/-are-utility-classes-evil-closedAuberbach
How is that any different to putting them in a class and importing?Beading
T
0

When I can't add a method to a class (say, Account is locked against changes by Jr. Developers), I just add a few static methods to my Utilities class like so:

public static int method01_Account(Object o, String... args) {
    Account acc = (Account)o;
    ...
    return acc.getInt();
}  
Tita answered 27/7, 2010 at 3:50 Comment(3)
Looks like you are the Jr one to me.. :P The non-evil way to do this is to politely ask your "Jr Developer" to unlock the Account file. Or better, go with non-locking source control systems.Overmeasure
I assume he meant that the Account file was locked such that Jr. Developers can't make changes to it. As a Jr. Developer, to get around it, he did as above. That said, still better to just get write access to the file and do it properly.Rib
Adding +1 to this because downvotes seem harsh when noone has the full context of why you supplied this answerPlane
O
0

Utility classes are not always evil. But they should only contain the methods that are common across a wide range of functionality. If there are methods that are only usable among a limited number of classes, consider creating a abstract class as a common parent and put the methods in it.

Offence answered 7/2, 2018 at 12:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.