HelperPattern/CodeSmell
Context:
- One or more additional operations on a type are needed beyond what the type's interface supports.
- The language/platform you're working in does not allow dynamic modification of types as by a MetaObjectProtocol
- Refactoring the type is inhibited because your project/team/company lacks CodeOwnership over the type:
- MoveMethod is impossible because you can't modify the definition of the type.
- IntroduceLocalExtension is not an option because either you can't modify the code that creates instances of the type or the type forbids extension (final, sealed, or otherwise not inheritable)
- WrapperPattern is painful because you have to write huge amounts of wrapping and unwrapping code dispersed throughout your project to make it work
Therefore: collect the operations as
ForeignMethods into an auxiliary class or module. It's the wrong place for them from an
ObjectOriented point of view, but at least it's the
only such wrong place they end up.
Resulting context:
- The operations against the type are split into two places. Better than repeating stuff all over the place, but not perfect.
- You don't have to touch the code for the offending type.
However:
This is a pattern in the sense that it is often a "least smelly" approach to the problem, and tends to reduce to a
LanguageSmell, typically the odor of compilation-fixed types with overtones of binary delivery. A few languages manage to reduce or nullify the smell:
- DelphiLanguage provide special support for this in the form of the 'class helper' declaration, the methods of which appear as additional methods on the targeted type.
- ObjectiveCee allows method categories to be tacked onto a type so long as they don't add fields
- CeePlusPlus can usually be hacked to add non-virtual methods without breaking binary compatibility, but the cure may be worse than the disease
- HaskellLanguage provides type classes, which are 100% statically checked and ensured by the compiler, but you're free to define your own type classes and declare instances of them, for any type, in any module. This allows you to literally add functionality to any data type you need, as it's needed, without violating the type safety already established in other modules. But this might not be the same kind of situation as in the article. The article talks mainly about OO languages which have a certain set of methods that "belong" to each class. In a functional language like Haskell, all you have is plain functions; they do not "belong" to a type; of course you can always define new functions anywhere that operate on whatever types they want. Not sure if this counts as providing new functionality for the type.
- NiceLanguage does away with the call-time distinction between member methods and utility methods; x.f(y) could have been declared as f(y) within x or as a free f(x,y).
- CsharpLanguage version 3 supports (non-virtual) helper methods where the first argument is tagged with 'this' as a modifier. If you need virtual helper methods you're stuck writing a TypeCase.
Abuse of helpers lampooned by TheCodelessCode?: http://thecodelesscode.com/case/155
CategoryPattern