Think about the larger projects you’ve worked on during your career. Did you have a dumping ground for odd pieces of functionality? Perhaps a utility class or even multiple multiple utility classes? It’s a very common thing to see a utility class in any codebase of reasonable size, especially one that has been around for a while.
Often there is just one utility class. Sometimes there are multiple utility classes. In some .NET projects, I’ve seen an entire assembly of otherwise unrelated utility classes. Typically these are a grab bag of useful stuff that doesn’t seem to have anywhere else to live. Almost universally, these utility collections become a festering mess of spaghetti code and a regular source of subtle bugs.
There is a better way. There are better places for the functionality to live - locations that are easier to discover, approaches that easier to use, structures that are easier to test, and code that’s easier to maintain.
One technique is to replace static methods with extension methods: If your utility class has a set of methods that all work on a common type, consider extracting them all out into an extension class for that type.
For example, on one project I worked on years ago, we ended up with a set of utility methods for working with strings. While that project was written in Delphi, here are some C# examples:
A key clue here is the presence of a common first parameter - they all take a string as the first parameter, and they’re all more focused on string manipulation than on anything else - an example of the feature envy code smell (see the famous Refactoring book for more detail).
Another clue is the way they all have
String as a part of their name.
In a modern environment, we should replace those methods with new extension methods hosted by a dedicated
StringExtensions static class:
Note the use of C# 7 tuple syntax for the renamed method
SplitAt(), which now returns both pieces instead of modifying the original.
It’s important when doing this to ensure that the
Extensions class remains tightly scoped, else it falls into the pit of failure and becomes another utility class collecting cruft.
Perhaps the best way to do this is to ensure that the extensions contained by the class are all tightly focused on the same base type - hence the name
StringExtensions, above. All other extension methods would live on other extension classes, with names based on the base type they extend - perhaps