Let’s be honest: how many times have you found yourself writing a custom static helper class, or putting your hands eagerly on one that came bundled with a framework, from the many available out there? If you’re anything like me, the answer will be at least … a few (remember that the first step to healing is admitting you have a problem). Static helpers seem to be a great idea at first glance, as they’re reusable components that don’t require any kind of expensive instantiation for doing common tasks, such as determining base URLs and paths or validating incoming data. But the sad and unavoidable truth is in many cases they’re simply wrappers for procedural code, which has been elegantly hidden behind a “class” construct.
So what's wrong with this? Well, even in the most harmless situations, when you use a static helper that produces a deterministic output, you’re actually throwing away the advantages that OOP provides. First and foremost, you can’t exploit the functionality of Polymorphism, which is a serious drawback indeed. Also, while it’s possible to further extend a static helper via Inheritance without infringing the Liskov Substitution Principle, you can’t create new helpers without breaking up existing client code, hence violating the Open/Closed Principle.
This doesn’t mean, though, that you should stop using your current helpers or completely avoid building new ones in the future. However, with a little willpower and the use of good object-oriented practices, you can easily create “instantiable” helpers. These will not only be shielded from the aforementioned issues, but will also be able to be injected into other objects’ internals. Add to this the ability to be tested in isolation, mocked up and so forth, and you’ll end up with a firm groundwork that will let you build more robust and solid OO applications.
As usual, the best way to demonstrate why static helper classes can make your life a lot harder (and how easy it is to turn them into “injectable” instances) is by example. In this two-part tutorial I’ll be setting up a few in PHP (which can be ported to other languages, of course) to help you grasp the pitfalls of this approach, and how to fix them.
Building a Static Validator Class in PHP
As I explained at the beginning, it’s fairly simple to prove why static helper classes can cause a lot of headaches, especially when building scalable object-oriented applications. The first step of this proof is to create a trivial helper that will be responsible for validating different types of input data, such as integers and floats, URLs and email addresses.
Please take a look at my sample validator helper, whose implementation looks like this:
There's not much to say about the above “Validator” class, as it only implements a set of static methods, which use a few PHP filters to validate the previously mentioned data types. However, don’t let the naive nature of this static validator fool you, since it comes with some issues worth noting. First, it’s nothing but a bag of procedural functions, which don’t expose explicit pre/post conditions or even class invariants.
Secondly, the only way to extend its current functionality is by means of Inheritance. This naturally infringes the commandments of the Open/Closed paradigm, as no instances that adhere to some interface can be passed around. And last but not least, the class is clearly doing too many things, hence breaking the Single Responsibility principle.
For obvious reasons, my seemingly inoffensive helper class is in reality a wild creature running through distant woods instead of treading the straight and narrow road of good object-oriented practices. For the sake of completeness, however, below I wrote a simple script that shows how to use it for checking some trivial data. Here it is:
// include the validator helper
use Library\Helper\Validator as Validator;
// validate a URL
// validate an email address
// validate a float number
// validate an integer number
/* displays the following
The supplied value is an invalid integer number.
Even though the helper actually does its business as one might expect, it’s clear to see its procedural roots. But, there’s no need to start pulling our hair out, as turning the class into a fully-extendable, object-oriented compliant structure is a simple process. In fact, the entire “conversion” is reduced to breaking the class into several instantiable subclasses, which will be implementers of the same interface.
blog comments powered by Disqus