NEWSFLASH: I believe I have found a use for class helpers in the wild!
This, even though I have long held the view that class helpers should not be viewed as a general purpose utility. They were designed for a very specific purpose and the authors of the technology themselves tell us how they are intended to be used. Using them in other ways is asking for trouble: they can break your – or others – code, they hide details of an implementation in A Bad Way™ and you are likely to run into limitations because what you want to use them for doesn’t tally with what they are intended to be used for.
But, somewhat to my surprise, I have recently found what I consider to be a legitimate use for them. Less surprisingly, it actually fits with their intended use.
First, The Context
I am currently implementing a client/server system. Not an SQL client/server, but a REST client server. That is, a Delphi application server which exposes services to a Delphi GUI client using REST to invoke those services and JSON to transport object representations between client and server.
I have classes that are used in both client and server contexts. For example, I have a class representing a property list – a list of named and typed values.
A client receives a property list in response to some request to the server in the form of a JSON array, and also it sends propertly lists to the server as part of a request. Obviously the ability for a property list to serialise to/from JSON is necessary in both client and server contexts for this class, and so it is an intrinsic behaviour to that class.
However, the client has other things that it is useful to do with an instance of that class.
For example, when updating a form in the GUI, it will wish to apply string values from a property list to edit controls, booleans to check-boxes/radio buttons, enum value to combo-boxes.. etc etc.
Clearly, putting methods to assist with these behaviour on the class is inappropriate since in the server context such methods have no use what-so-ever and would only pollute the interface on the class.
But having a client-side specific sub-class is also over-kill. I do not want to have remember that when coding in the GUI I use the TClientPropertyList rather than TPropertyList. Equally I do not really want to have to invoke cumbersome first-class procedures such as SetCheckboxFromProperty( propList, checkbox ) when the more intuitive propList.SetCheckbox( checkbox ) could be made available.
Class helpers actually have a role here, and it fits the original vision perfectly.
A Class To Help – A Class Helper
Let us briefly revisit the vision for class helpers, in the form of the documentation:
You can use the class helper any place where you can legally use the extended class. The compiler’s resolution scope then becomes the original class, plus the class helper.
Class helpers provide a way to extend a class, but they should not be viewed as a design tool to be used when developing new code. They should be used solely for their intended purpose, which is language and platform RTL binding.
One thing to be wary of is the directive w.r.t using them as a design tool for new code, but in my case I believe that the fact that I am employing the helper to assist with “platform RTL binding” overrides this concern. I am creating a platform – my application architecture – and what I am doing by employing a class helper in this case, is extending a class in a particular, and unambiguous, context within that platform.
That is, I am creating an extension for a class which is relevant only to client code on my platform.
Furthermore, I “own” both the original class and the context in which it is being used. There is no risk here that my helper will break or otherwise interfere with another helper for the same class. If I ever need to embellish the original class, I will simply do so, and ensure that the client-context helper provided by the platform is updated accordingly, if necessary.
The Resulting Architecture
What I ended up with was 2 units:
Project.PropertyList; // Contains the TPropertyList itself Project.Client.Utils; // Contains the client-side TPropertyListHelper among other things
The server project obviously doesn’t employ any of the Project.Client.* units. The client projects employ both the property list but then also use the client specific Utils unit if/when required.
When I am coding up the GUI for my project in the client, I add Project.Client.Utils to my uses list and my TPropertyList instances are now endowed with GUI/client specific methods to help get/set values from GUI controls. If my client code is not actually concerned with GUI aspects, then it simply works with TPropertyList as-is, without even bringing the helper into scope if not required.
When I am coding up the server-side code, I simply work with TPropertyList itself.
And if I ever need any server-side specific extensions to the class (for example, adding methods to persist a propertly list in an SQL database on the server-side), I could introduce a server-side helper into Project.Server.Utils.
So there is a valid use for class helpers, but I still maintain that using a class helper to extend/embellish classes from *other* libraries/frameworks is A Bad Idea™, because you potentially won’t be the only person with an idea of what makes a good extension/embellishment to that class. As soon as you share code with someone who has different ideas, one or other of you is going to have to change their ideas and, potentially, a great deal of code.