I hate first episodes

I have seen Scrubs five times, I have seen almost every episode of Community twice and I can remember watching the Nanny countless times when I was lying sick in bed.

But I remember none of the first episodes, the reason is I hate first episodes. They are not the real deal. They are somewhat like the rest of the episodes, but then again not that good. I see this blog post in analogy to all the first episodes of all the shows I loved over the years. This Blog Post will hopefully be a good start but if you read it five years from now (December 2018) you will think to yourself "oh boy this post is somewhat awkward".

Having this out of the way let’s begin with the topic of this Post. It is somewhat of a rehearsal if you are a programmer/developer for many years, although I’ve seen the code of some programmers who are in the field for over five years, who still struggle with this. It’s about utility classes, helper classes, dirty little helpers and what you fancy to call them.

What are utility classes?

Let us begin with a short definition of a Utility class.

  • Utility classes contain only static Methods

  • Utility classes are static classes and/or do not have a public constructor

  • Utility classes shouldn't have state

  • Utility classes do many jobs which you have to perform in many classes throughout your code.

Let's examine the usage of a class and see if it aligns with a utility class

Normal classes contain a constructor and you can instantiate them. Utility classes do not contain a constructor. Classes are build of fields, properties(getters and setters), methods and constructors. Utility classes are built of static methods and do not contain any fields. Classes can be inherited.
Utility classes cannot be inherited due to their lack of a constructor or methods that belong to an object.
In conclusion, we either must revise our view of what a class is or a utility class is no class.

Take this code for example.

public static class PathUtils
{
    public static string AddPathSeperatorIfNeeded(string path)
    {
        if (EndsWith(path, Path.DirectorySeparatorChar))
        {
            return path;
        }
        return path + Path.DirectorySeparatorChar;   
    }

    private static bool EndsWith(string text, char character)
    {
        return text.EndsWith(character.ToString());
    }

    public static string Canonicalize(string path)
    {
        string canonicalized = path.Replace('/', Path.DirectorySeparatorChar);
        return canonicalized;
    }
}

class Program
{
    static void Main(string[] args)
    { 
        string basePath = "D:\\DevEnvironment";
        string projectName = "PathUtils";

        string fullPath = PathUtils.AddPathSeperatorIfNeeded(basePath) + projectName;
        string fileName = "/help.md";

        fullPath = fullPath + fileName;
        fullPath = PathUtils.Cannonicalize(fullPath);
        Console.WriteLine(fullPath);
        Console.Read();
    }
}

This is clearly not a conventional class but it is useful in many cases. Ok, you could argue about the implementation but then again it encapsulates logic in a method and it is not present in ten different classes right? …Right??... …

Arguments against utility classes

Yeah somewhat right. If you struggle with good architecture, then you will find the source code in more than one place. I can’t prove this statement but you could try it for yourself, just search your company's code base with grep. Search for the method header of a simple utility method and see the result. If you encounter the same method header more than once, bingo you found an accident waiting to happen.

Why am I so sure that you will have duplicate code?

  1. I have written duplicate more than once. I started programming, learnt how to do the job and how to get it done quickly. A project starts simple but over time you will have deadlines to catch and sometimes it is easier to copy and paste a method than to figure out where the code belongs to.
  2. I have found duplicate code lots of times. I don’t work for a big major company with pair programming, big testing departments and hallway usability test. Sometimes code quality is lost in the rush. I am not saying that we don’t “repair” and clean up afterwards but sometimes those little utility classes are to dang easy to leave in the code and nevertheless, the code runs fine.

You could now say that this argument is weak, because its a problem of the architecture and not of the utility class itself. True, but are there any other downsides besides possible duplicate Methods?

Encapsulation

Yes, there are, think about the state of the application. Objects should encapsulate their state so that no other classes can manipulate the precious internals of our objects. Utility classes change the state of our objects and make them so vulnerable to change from outside.

Coupling

You either have to couple your objects against the Utility class or the utility class against your Objects. It doesn’t matter what you choose, both options come with problems.
Utility classes are normally static and cannot be instantiated which in conclusion means you cannot derive from them(In Java this means the class is final and in C# it means its sealed or static)

Testing

You should consider that you can not test your classes in isolation from your utility class. This argument seems to be more valid when accessing the filesystem and not so much of a problem if you use something like Math.Add() or Math.Max().

Static produces static!

Utility classes tend to encourage programmers to write more procedures instead of objects with well-encapsulated data, in other words, static methods produce more static methods.

Are utility classes all bad though?

To be frank, utility classes can be helpful at some point and you shouldn’t throw them out of the window but if you can avoid them you should do so. I use a string utility class to handle all the stuff in my code that would normally belong on the string. If I was the creator of the string class I would add them directly to it, but since I am just a user of it I try to add this features the best I can. There are some ways to handle this problem of classes that lack a needed feature but all come with certain downsides to it.

So what is the alternative?

The simple answer, I don’t see any good alternative if the time is limited and the customer is in need of a solution, but if you have enough time and perhaps experience, you will be able to place those nifty little methods in a good class structure. A good day or two of refactoring might do also the job. Sometimes a little will do a lot. If your utility class is mostly an extension, you could use inheritance and extend the class.

I have seen static methods that were rebuilt to a class and this seems to be the best way to resolve issues with utility classes. Having said this, we should ask ourselves when to replace utility classes or rather methods in it with objects.

Think about change

If your procedure is changing, then you should consider using objects instead. Changing a procedure is dangerous, it will lead you towards errors. Instead of changing you should consider extending. You can replace an object instead of changing a procedure.

If there is more than one scenario in which a utility method is used, you probably could better handle it with objects. You recognize such methods on their structure. Look at this method for example

public static int HandleXYZ(bool isSpecialCase)
{
    int val = 0;
    if (isSpecialCase)
    {
        //do something
    }
    else
    {
        //do something else
    }
    return val;
}

Arguments for Utility classes

Let’s take a look at some good examples of utility classes. In C#, Java and Javascript we find the Math class which contains methods/functions to calculate. There is no need to instantiate this class and a derived class would probably never overwrite a Math function (Why would you want to overwrite the add function?). It seems that utility classes have their right to exist even though they are frowned upon. When using Utility classes we should always be aware that we are breaking some concepts of object-oriented programming. Object-oriented programming is, in my opinion, a very pleasant way of writing and thinking code and I try to avoid the procedural approach of utility classes as best as I can, but on the other hand you have clients which tend to expect the software to ship at a certain point even though it is not as beautiful engineered as it could be.

Sometimes there are only two options Ship it as is or die beautiful

Considerations when writing Utility classes

When building utility classes there are some key points one should consider. First, there should be no state/side effects! If you call a method, it should always return the same result, this means func() == func().
Try to keep the methods short and simple like all other methods.

If the methods in your utility class tend to change often, you can probably better work with an object.
Does your utility class alter and work with a data structure? You can combine both to get nice proper objects that encapsulate the data.

I hope this provided some value to you and if so I hope you hang around for the next article

Thanks for reading

Julian

Appendix

Let's assume that our example code above should be altered to also support Unix paths.
This is a great opportunity to restructure the code.

In a first attempt, we rebuild the code into an object

    public class WindowsPath
    {
        private string path;

        public WindowsPath(String path)
        {
            this.path = path;
        }

        public string AddPathSeperatorIfNeeded()
        {
            if (EndsWith(path, Path.DirectorySeparatorChar))
            {
                return path;
            }
            return path + Path.DirectorySeparatorChar;
        }

        private bool EndsWith(string text, char character)
        {
            return text.EndsWith(character.ToString());
        }

        public string Canonicalize()
        {
            string canonicalized = path.Replace('/', Path.DirectorySeparatorChar);
            return canonicalized;
        }
    }

Now that we have an object we can rethink the structure to match the new requirements

public abstract class BasePath
{
    protected string path;

    public abstract string AddPathSeperatorIfNeeded();
    
    protected bool EndsWith(string text, char character)
    {
        return text.EndsWith(character.ToString());
    }

    protected bool StartsWith(string text, char character)
    {
        return text.StartsWith(character.ToString());
    }

    public abstract string Canonicalize();

    public void AppendPath(string append)
    {
        if (StartsWith(append, Path.DirectorySeparatorChar) || StartsWith(append, '/'))
        {
            append = append.Remove(0, 1);
        }
        path = this.AddPathSeperatorIfNeeded() + append;
    }
}


public class WindowsPath : BasePath
{
    public WindowsPath(String path)
    {
        this.path = path;
    }

    public override string AddPathSeperatorIfNeeded()
    {
        if (EndsWith(this.path, Path.DirectorySeparatorChar))
        {
            return this.path;
        }
        return this.path + Path.DirectorySeparatorChar;
    }

    public override string Canonicalize()
    {
        string canonicalized = this.path.Replace('/', Path.DirectorySeparatorChar);
        return canonicalized;
    }
}


public class UnixPath : BasePath
{
    public UnixPath(String path)
    {
        this.path = path;
    }

    public override string AddPathSeperatorIfNeeded()
    {
        if (EndsWith(this.path, '/'))
        {
            return this.path;
        }
        return path + '/';
    }

    public override string Canonicalize()
    {
        string canonicalized = this.path.Replace(Path.DirectorySeparatorChar, '/');
        return canonicalized;
    }
}

static void Main(string[] args)
{
    string basePath = "D:\\DevEnvironment";
    string projectName = "StringUtils";

    string fullPath = PathUtils.AddPathSeperatorIfNeeded(basePath) + projectName;
    string fileName = "/help.md";

    fullPath = fullPath + fileName;
    fullPath = PathUtils.Canonicalize(fullPath);
    Console.WriteLine(fullPath);
    Console.Read();


    BasePath path = new UnixPath("/home/Julian/DevEnvironment");
    PrintPath(path);


    path = new WindowsPath("D:\\DevEnvironment");
    PrintPath(path);
}

private static void PrintPath(BasePath basePath)
{
    string projectName = "PathUtils";
    basePath.AppendPath(projectName);
    string fileName = "/help.md";
    basePath.AppendPath(fileName);

    string fullPath = basePath.Canonicalize();
    Console.WriteLine(fullPath);
    Console.ReadLine();
}

We created an abstract base class which contains some basic behaviour. The UnixPath and the WindowsPath implement the rest of the behaviour (we could alter the code even more but for the sake of this argument this should be enough). In a scenario like this, the restructuring of the code is helpful.

Go back
This page uses cookies: READ MORE ;