次の方法で共有


Are Helper Classes Evil?

First off, a definition: A helper class is a class filled with static methods.  It is usually used to isolate a "useful" algorithm.  I've seen them in nearly every bit of code I've reviewed.  For the record, I consider the use of helper classes to be an antipattern.  In other words, an extraordinarily bad idea that should be avoided most of the time.

What, you say?  Avoid Helper Classes!?!  But they are so useful!

I say: they are nearly always an example of laziness.  (At this point, someone will jump in and say "but in Really Odd Situation ABC, There Is No Other Way" and I will agree.  However, I'm talking about normal IT software development in an OO programming language like C#, Java or VB.Net.  If you have drawn a helper class in your UML diagram, you have probably erred).

Why laziness?  If I have to pick a deadly sin, why not gluttony? :-)

Because most of us in the OO world came out of the procedural programming world, and the notion of functional decomposition is so easy that we drop to it when we come across an algorithm that doesn't seem to "fit" into our neat little object tree, and rather than understand the needs, analyze where we can get the best use of the technology, and place the method accordingly, we just toss it into a helper class.  And that, my friends, is laziness.

So what is wrong with helper classes?  I answer by falling back on the very basic principles of Object Oriented Programming.  These have been recited many times, in many places, but one of the best places I've seen is Robert Martin's article on the principles of OO.  Specifically, focus on the first five principles of class design. 

So let's look at a helper class on the basis of these principles.  First, to knock off the easy ones:  

Single Responsibility Principle -- A class should have one and only one reason to change -- You can design helper classes where all of the methods related to a single set of responsibilities.  That is entirely possible.  Therefore, I would note that this principle does not conflict with the notion of helper classes at all.  That said, I've often seen helper classes that violate this principle.  They become "catch all" classes that contain any method that the developer can't find another place for.  (e.g. a class containing a helper method for URL encoding, a method for looking up a password, and a method for writing an update to the config file... This class would violate the Single Responsibility Principle).

Liskov Substitution Principle -- Derived classes must be substitutable for their base classes -- This is kind of a no-op in that a helper class cannot have a derived class. (Note my definition of a helper class is that all members are static).  OK.  Does that mean that helper classes violate LSP?  I'd say not.  A helper class looses the advantages of OO completely, an in that sense, LSP doesn't matter... but it doesn't violate it.

Interface Segregation Principle -- Class interfaces should be fine-grained and client specific -- another no-op.  Since helper classes do not derive from an interface, it is difficult to apply this principle with any degree of seperation from the Single Responsibility Principle. 

Now for the fun ones:

The Open Closed Principle -- classes should be open for extension and closed for modification -- You cannot extend a helper class.  Since all methods are static, you cannot derive anything that extends from it.  In addition, the code that uses it doesn't create an object, so there is no way to create a child object that modifies any of the algorithms in a helper class.  They are all "unchangable".  As such, a helper class simply fails to provide one of the key aspects of object oriented design: the ability for the original developer to create a general answer, and for another developer to extend it, change it, make it more applicable.  If you assume that you do not know everything, and that you may not be creating the "perfect" class for every person, then helper classes will be an anathema to you.

The Dependency Inversion Principle -- Depend on abstractions, not concrete implementations -- This is a simple and powerful principle that produces more testable code and better systems.  If you minimize the coupling between a class and the classes that it depends upon, you produce code that can be used more flexibly, and reused more easily.  However, a helper class cannot participate in the Dependency Inversion Principle.  It cannot derive from an interface, nor implement a base class.  No one creates an object that can be extended with a helper class.  This is the "partner" of the Liskov Substitution Principle, but while helper classes do not violate the LSP, they do violate the DIP. 

Based on this set of criteria, it is fairly clear that helper classes fail to work well with two out of the five fundamental principles that we are trying to achieve with Object Oriented Programming. 

But are they evil?  I was being intentionally inflammatory.  If you read this far, it worked.  I don't believe that software practices qualify in the moral sphere, so there is no such thing as evil code.  However, I would say that any developer who creates a helper class is causing harm to the developers that follow. 

And that is no help at all.

Comments

  • Anonymous
    September 06, 2005
    Hmmm.... interesting. But I'm not sure that I agree with your definition.

    So if I take a helper class and convert all static methods to instance methods, does that now make it less evil? I create all of my "helper classes" in such a way that they need to be instatiated and passed references to the particular instance of the class that they are "helping". Would this still be considered evil by your standards?

  • Anonymous
    September 06, 2005
    Nice to see someone from Microsoft making a stand against this anti-pattern! Now maybe you could have a word with some of your colleagues :-)

    Jim

  • Anonymous
    September 06, 2005
    This is all good and all, but if helper classes are undesireable, where do you put miscellaneous code that you would normally put in a helper class? Where does that method go that say, helps encode a URL? Or that function that converts an array to a comma delimited list? Or in C#, that wrapper for a certain Win32 API?

    In all practicality, I find that I often have these piddely tasks that don't really lend themselves to encapsulation within an object and are pretty much on their own, but are called from myriad places.

    If I was coding in C++, I would place it outside of a class, but languages like C# and VB.NET don't allow me to do that. Does it violate OO principles? Of course it does. But OO principles are not meant to be a straightjacket.

    All that said, I do agree that if they show up in a UML diagram, your helper class is probably doing more than isolating a "piddely task" and that something is probably wrong.

  • Anonymous
    September 06, 2005
    I can think of one case where helper classes are useful... Code re-use in a company. For instance, a company has a policy on how its programs will access and write to the registry. You wouldn't want some products in the company saving its data in HKLM/Software/CompanyName/ProductName and some under .../Software/ProductName and some .../"Company Name"/"Product Name". So you create a "helper class" that has static functions for accessing data in the registry. It could be designed to be instantiatable and extendable, but what would be the advantage? Another class could implement the companies' policy on encryption, another for database access; each could be created in a similar way, under different namespaces in the same DLL. That package can then be used by each developer. I would agree that having a single catch-all for all of these functions would not be good OO, but this method would still violate some of the principles you outlined in your post...

  • Anonymous
    September 06, 2005
    I'll try to answer more than one comment in this reply:
    To Matt: would making the methods non-static... would that be less evil? Yes. You are no longer in violation of the Open-Closed Principle. Would the ability to pass the object to the classes that use it make it less evil? Yes: you are now using Dependency Injection. It is also a lot more testable.

    To Nate: I appreciate your practicality. However, I don't agree that there are many cases where code is truly miscellaneous. Encoding a URL is part of a URL object. Converting an array to a comma-delimeted list is an extension of the array object itself. Wrappers for API calls are usually best when created as private methods inside the class that needs to call the API.

    To William Sullivan: The largest reusable code base that all your developers are already using is the .Net Framework. The vast majority of the framework uses instantiated classes, not static helper classes (there are exceptions, unfortunately). So, the need for reuse is NOT a good argument for helper classes.

  • Anonymous
    September 07, 2005
    I agree that ideally, URL encoding should be in a URL object and so on, but what about the practical scenario where objects like URL and array objects are provided by the framework, and one does not have the ability to modify the framework?

    Take say the .NET array object; I want to use that object but I do not have the ability to add a method that does a custom parse job. Subclassing the array to simply aggregate behavior without modifying existing behavior strikes me as overkill.

    I emphatically agree that 99% of the time, it probably isn't necessary to use a helper class, and that most of the time that are abused; but that does not detract from the other 1% of the time.

    As for William Sullivan's argument, I'd like to hear how you would approach the specific scenario he proposed, as that is exactly the kind of scenario for which I find helper clases useful. I'm honestly curious.

  • Anonymous
    September 07, 2005
    Just to add another example, say that you are writing .NET code to retrieve the string for a Win32 error message. This requires calling the Win32 API FormatMessage() and doing some non-trivial munging on the results to get the string into a .NET string.

    This is best done in a helper class because there really isn't any object to instantiate per se, and it is non-trivial so you would not want to duplicate this code in every place that you would want to use it. Granted, you could write code that looked like this:

    wec = new Win32ErrorCodeConverter();
    string message = wec.Convert(errorCode);

    ...but this would result in an instance object that is no different than any other instance; a gross misuse of OOP.

    Another good example of a helper class in .NET is the System.Convert class. The only other place I could imagine putting that functionality would be on the root Object class itself.

  • Anonymous
    September 07, 2005
    William and Nate:
    Please see my follow-up post:
    http://blogs.msdn.com/nickmalik/archive/2005/09/07/462054.aspx

  • Anonymous
    September 07, 2005
    William and Nate:
    Please see my follow-up post

  • Anonymous
    September 10, 2005
    I came across a couple of good posts here and here on helper classes.
    Although I don't believe helper...

  • Anonymous
    August 21, 2006
    I never like too much the concept of "helper classes",   First of all I tried to found some written...

  • Anonymous
    September 08, 2007
    PingBack from http://mikesdump.wordpress.com/2005/09/10/the-evils-of-helper-classes/

  • Anonymous
    August 05, 2008
    PingBack from http://blog.guru-php.com/2008/08/helpers-are-unhelpful/

  • Anonymous
    April 02, 2009
    PingBack from http://bigballofmud.wordpress.com/2009/04/02/helpers-and-managers/

  • Anonymous
    November 01, 2013
    Pingback from  When are anti-patterns acceptable patterns? | Technology & Programming Answers

  • Anonymous
    February 09, 2014
    Pingback from  Type Safe C# « Non Geek for the Geeks

  • Anonymous
    February 09, 2014
    Pingback from  Type Safe C# « Non Geek for the Geeks

  • Anonymous
    February 09, 2014
    Pingback from  Type Safe C# « Non Geek for the Geeks

  • Anonymous
    February 09, 2014
    Pingback from  Type Safe C# « Non Geek for the Geeks

  • Anonymous
    February 09, 2014
    Pingback from  Type Safe C# « Non Geek for the Geeks

  • Anonymous
    February 09, 2014
    Pingback from  Type Safe C# « Non Geek for the Geeks

  • Anonymous
    February 20, 2014
    Pingback from  Type Safe C# « Non Geek for the Geeks

  • Anonymous
    May 08, 2014
    Pingback from  Why helper, singletons and utility classes are mostly bad - Smart421

  • Anonymous
    September 19, 2014
    Pingback from  Extending a struct. Using helper classes rather than composition | Question and Answer