Extend classes or use Utils and Handlers instead?

Posted on March 23, 2012

0


Extending Classes is one of the knee-jerk reactions when you want to build and extend on default behavior.

Unproven and wild assumptions

This article explores a new – and more restricted – approach to OOP than I am used to until now. It describes a new approach I will be using and testing in the next period of time, while implementing new code and refactoring old solutions I created last year and the years before that.

I pose some wild assumptions in this post that might or might not make sense now and later.

The lazy programmer

The lazy programmer – Extending a Class is the work of a lazy programmer. While – in many cases – there is nothing wrong with that, in some cases it can lead to spaghetti-code that is hard to debug.

The clean programmer

No overrides, no more than 2 layers – To create clean code, you need to make some extra effort. Effort I do not take some times either. This extra effort in the case of this article can be translated into two rules:

No overrides – Using no overrides means that when your specific implementation deviates from the basis, you will have to find another solution that produces the same results

No more than two layers – That is: in your code. If you need more than two layers to achieve your goal, you need to be very sure there is no other solution that is simpler and more clear.

Keeping it simple

When I code, I strive to keep things simple. Less is more. Less is better. Less can be:

  1. Less code – Achieved by cutting out repetition, using reflection and utilities
  2. Less Classes – By avoiding certain design principles that look good in theory but are shit in practice and by making shortcuts that might seem “dirty” if you believe some code-gurus, but have been proven to deliver rock-solid code.
  3. Less amount of layers – The more you layer, the more abstract things become.

Clean code is clear code is simple code

I want better code – The entire goal is to produce code that is on a newer and higher level of simplicity as what I write right now. Compared to what I see happening specifically in Java projects, that code already is quite clean. And looking at how projects go where I did the ground work, there already is a solid basis. But I still find some solutions I create quite dirty and unclear. In brief: I want better code.

Clean code is clear code is simple code – The moment code becomes simple and clear, it becomes clean. It means that each action is explicit. Each chain of actions is easy to follow and easy to follow back. It means that when you reach the end of the chain, you can still remember the steps you took to get there. And if there were warp-gates, the experience is fast, clean and clear instead of the colorful LSD trip at the end of: “Space Odyssey 2001”.

Interfaces, Utils and Handlers

Interfaces: make all look the same – Interfaces help to create several separate classes

Util basis: code snippets – Utils are snippets of code that perform specific actions. A Util can change lower-case to upper-case or translate a String-coded Date/Time object (20010323 12:56:23″) to a concrete Date Object and vice versa.

Handler basis: more complex actions – Handlers deal with more complex actions, like parsing through lists and converting complex data.

Start: extract concrete actions into external classes  – While you – at first – might choose to implement functionality within your concrete classes, Utils invite you to move this functionality into external Util-classes which have no state and can be (in my implementations) addressed via Static methods.

Next: use Handlers and Utils to perform the actions – Instead of using code inside your classes, you address the Utils and Handlers to do the actual work. Instead of extending a Class and overriding functionalities, you implement Interfaces, Handlers and Utils.

Promoting re-use – One of the direct benefits of extending code to util-classes is that all your actions become easier to re-use. That one parser for specific code or specific data becomes public property you can access from all over your project.

Design Patterns that support this approach

Strategy – The Strategy Pattern extracts only these parts of the code in a class which are variable. For instance: to handle a specific process. Each strategy is put in a separate class and injected into the Contect object.

How to use: extend, but avoid Deep Layering

Extending classes reduces repetition – One good thing about extending classes is that it reduces repetition. Once you solved one specific problem, you can re-use that solution in the subclasses by the methods you expose from the super-class.

Extending with care: 2 layers is still ok, 3 and more is trouble – Extending is not bad. What is bad is extending extended classes. Each new layer in your cake adds more vulnerabilities in your code due to implemented exceptions, increased abstraction (why and what is happening in the code I do not see?) and more possible deviations that might collide at a certain way (where Layer 1 allows, layer 2 does partially and layer 3 needs to work around the exceptions of layer 2 to implement the solutions that were present in layer 1).

Layer one: your base class -This is where all your basic actions take place. The stuff you want to re-use.

Layer two: your concrete implementation / your exception – This is where you do the stuff you really need. Either by directly using and accessing the functionalities in your Base Class, or by accessing part and overriding others.

Layer three: trouble – When you extend an extension on your Base Class, you basically create a different version of a concrete implementation. While parts are the same, other parts deviate. This is cool if your implementation is straight-forward. However, as mentioned before, when Layer 2 also includes exceptions, these exceptions can lead to unwanted behavior due to the abstraction of functionalities. Either by things not being executed while you expect them to, or later, by changes in Layer 2 that unintentionally impact the implementation in Layer 3: leading to newly introduced errors you will only see in run-time.

Delegating the concrete execution to Handlers and Utils – Instead of implementing your concrete actions within your Base Class, you can consider to extract them to separate Handlers and Utils.

More portable solutions: call the extracted code – The main benefit of extraction is that your solutions become more portable. Instead of a Base Class you need to extend, you can choose to call the extracted code inside the external Handlers .

No dependency on the Base Class –  To make things work and implement specific functionalities, you do not have to rely on your Base Class, but instead you call on the Handlers and Utils.

Less layers and explicit execution – Whatever you do can become more explicit. Where Layer 3 in your subclassing-scheme relied on Layer 2 which relied on your Base Class in Layer 1, now you perform actions directly on the Handlers and Utils you created and on the methods that perform the actions, without potential interference by overrides in Layer 2.

Summarizing: problems with extending/subclassing

Extending a Class comes with a certain cost: abstraction and loss of consistancy as you continue.

  1. What happens where? – When you extend classes that extend classes, you will get certain deviations: methods which are overridden and overridden in the next layers. While one branch will execute the default behavior, another branch of extensions will implement a slightly different approach.
  2. Layers and distance – Each new extentsion in the chain adds another layer that can add extra distance between you and the actual point where the execution of the actions is taking place.
  3. Exceptions and loss of consistency – While 80% of all extensions perform the same actions the same way, the remaining 20% is what might and sometimes will give the headaches when things go wrong and unpredictable. That is the code where – instead of implementing everything the same way, you do it slightly different. On a bad day, that code can cost you several hours just finding out what the fuck is going on where.
  4. Loss of overview – As some code will be executed on the Base Class (or a subclass you extended to create your class) that code is executed “Somewhere”.
Advertisements
Posted in: Uncategorized