Whenever it is mentioned, the Parameterize from Above (PfA) pattern is often discussed in connection with - or rather, as a counterpoint or antidote to - the Singleton pattern. Perhaps the other most recurrent feature of any PfA discussion is to note its lack of a proper written description [Deigh2007]:
Much has been written about the pattern identified by Kevlin Henney as PARAMETERIZE FROM ABOVE. Indeed, much has been written about it (just search the Web for ‘Parameterize from Above’ and ‘Parameterise from Above’), but as a pattern it has never been written up. Much has also been written on accu-general about how Kevlin should get around to writing it up properly!
Well, I can reveal that a short write-up of the pattern, forces and all, was due to appear in the German magazine JavaSPEKTRUM nearly five years ago. The 'Patterns in Java' column was in essence a continuation of the identically named column in the defunct Java Report, but in German thanks to Martina Buschmann's translating skills. The article containing the missing link and unwritten pattern was to have followed the article entitled 'One or Many?' [Henney2003]. Indeed, the raison d'être for 'One or Many?' was to set up the follow-on article by focusing on some of Singleton's problems. The follow-on article would then have presented two pattern descriptions: Parameterize from Above and then Singleton revisited in the light of Parameterize from Above. 2003, however, was quite a lean year for JavaSPEKTRUM, which ultimately survived by slimming down and reducing overheads. Alas one such overhead was the translation of the 'Patterns in Java' column. There were two notable consequences of the cost-cutting exercise: (1) a short but nonetheless documented form of Parameterize from Above never saw the light of day; (2) JavaSPEKTRUM survived, unlike its sister magazine Java Report two years before it.
This fourth instalment of 'The PfA Papers' takes time to explore some of the Singleton-related territory that in part motivates Parameterize from Above, including revisiting 'One or Many?' and some extracts from its unpublished and unfinished successor.
To set the scene, let's kick off with the draft opening of the unpublished article:
For many years I have used SINGLETON as an indicator of pattern and design maturity. Programmers who rarely use it either do not know about it by name - and hence are probably unfamiliar with design patterns - or they understand it fully - and hence understand the relationship between design and patterns. A few questions and some conversation usually differentiate one end of the scale from the other. However, there is a significant group of programmers that falls between these two extremes, and they are the principal users of SINGLETON. Without meaning to, many programmers are creating code that is difficult to evolve, hard to comprehend, awkward to test and resistant to change in the belief that they are following good practice. Why is the design assumed to be good? Because it has been documented in a book, Design Patterns, that is widely recognised as a purveyor of good practice. Trust in a design pattern is an important quality, but it should not be unquestioning. A critical eye is needed when evaluating any design.
Superficially, the Singleton pattern comes across as a simple idea [Gamma+1995]:
Ensure a class only has one instance, and provide a global point of access to it.
The associated class diagram for the pattern also looks simple enough: a single class. What could be simpler? Sadly, this apparent simplicity belies the accidental complexity introduced by the pattern. (It is both interesting and in some ways cautionary that many etymologies of the word singleton state that it is derived from single and patterned after simpleton.) The emphasis of the pattern's intent is often misread [Henney2003]:
The race to embrace the apparent convenience offered by second half of the sentence - '... global point of access...' - often eclipses the necessity of the first half and the classification of the pattern as a creational rather than a structural pattern.
The notion of providing a global point of access is seen by many as the main motivation for the pattern, whereas first and foremost SINGLETON is a factory pattern: it concerns the encapsulated creation of objects. What is it encapsulating? Instance control. In this case, to be precise, the existence of no more than a single instance - an exceedingly rare constraint in practice.
A quick examination of the majority of so-called SINGLETONs in code reveals that they are global variables and not SINGLETONs: they just happen to share some of the same solution structure, but not the same motivating problem, forces and consequences, all of which are required to correctly characterise a pattern. Most of these misapplications either do not enforce instance control or the uniqueness of the instance that they control happens to be a coincidence rather than a genuine constraint.
Part of the misreading of the pattern is down to the individual reader, and much is down to the cultural interpretation of the pattern, but some credit (or, indeed, debit) must also go to the original Gang of Four write-up. When compared to the other pattern descriptions in Design Patterns, the entry on Singleton seems surprisingly weak. It lacks the much of the detail and considered discussion that characterises the other patterns in the catalogue. For example, only benefits and no liabilities are listed for it, which seems not only surprising to the modern reader but is also out of step with the more even-handed appraisal of the other patterns in the catalogue. Similarly, no worked example is presented to motivate the pattern, only the following [Gamma+1995]:
It's important for some classes to have exactly one instance. Although there can be many printers in a system, there should be only one printer spooler. There should be only one file system and one window manager. A digital filter will have one A/D converter. An accounting system will be dedicated to serving one company.
No satisfactory explanation is offered for the opening sentence, which is also not quite right: the pattern constrains the number of instances to at most one, not exactly one. And no justification is offered for any of the examples, which are essentially incorrect for one reason or another. Much of the remaining pattern description is devoted to the mechanics of the pattern's solution rather than the understanding of the problem.
With this background - and in spite of its notable and intentional absence from the Gang of Four's list of 'simplest and most common patterns' - it is perhaps unsurprising that Singleton is most commonly applied as a souped-up global. The result is typically a design that has all the issues associated with globals, but without the relative simplicity. This situation inspired Kent Beck's full and candid (if somewhat flippant) write-up of Singleton [Beck2003]:
How do you provide global variables in languages without global variables? Don't. Your programs will thank you for taking the time to think about design instead.
The problems that Singleton can introduce into a design are not always immediately apparent, and nor is its misapplication. However, beyond the initial sugar rush of apparent coding convenience and cleverness, its subsequent inconvenience can manifest itself in a number of ways: it complicates testing, safe and simple threading, architectural configurability and pluggability, adaptation and evolution of code, design reasoning and code readability, application start-up and shutdown, and so on. Many applications and implications of Singleton's design lead programmers to come up with increasingly 'clever' solutions - books, magazines and a multitude of web pages offer a dizzying variety of cure-alls. These may sometimes reveal coding virtuosity, but they serve mostly to highlight a missed trick: if these workarounds seem to recur in the context Singleton, why not address the root cause rather than attempt to repeatedly and ingeniously mollify its effects?
Program to an interface, not an instance
One of the most useful guidelines and enduring sound bites from the Design Patterns is 'program to an interface, not an implementation' [Gamma+1995]. This encourages a style of design that is strongly encapsulated. Rather than working with glorified C structs dressed as classes with assembleresque getters and setters, develop classes that have rich, intentional public interfaces. Rather than working with class hierarchies rooted in classes that are mostly (or completely) concrete toolkits of default functionality, favour hierarchies rooted in IDL-style interfaces - interface in Java and C#, fully abstract classes in C++. In situations where duck typing is used, as in C++'s template system or the type systems of dynamic languages such as Ruby and Python, program according to the concept or protocol in question without mention or assumption of a particular concrete type. The resulting design style is loosely coupled, testable and refreshingly clear. But the consequences of this guideline do not stop there [Henney2003]:
We can extend this with a further principle:
Program to an interface, not an instance.
This second principle can be considered a deeper reading and consequence of the first.
This second principle was also the working title for the unpublished article, the draft of which included the following observation:
Knowledge of multiplicity should be encapsulated rather than shouted from the rooftops, hence PFA rather than SINGLETON, which litters the code with the assumption. A more complete failure of encapsulation it would be hard to find.
And also the following:
Which brings us to the question of the multiplicity constraint itself. One or many. SINGLETON focuses on the particular multiplicity constraint of 1, but it could in principle be any number N. The idea is that in a given situation the total number of instances of a particular class is constrained to a single instance. OK, but who is doing the constraining? The class itself or the situation that it finds itself in? In the SINGLETON pattern the constraint is expressed and enforced in the class. However, what determines the multiplicity of a given type of object in a particular scenario is just that: the details of the scenario. The coupling in SINGLETON is often back to front: the application that uses the class should constrain its instance count, not the class itself. So the general rule should be that if a given infrastructure or design situation demands an instance limit of N, the enforcement should be at that level and not within the instance type. The property belongs to the application, not the class.
The necessity of the instance constraint is one of the reasons I often refer to Singleton as the Highlander pattern - 'There can be only one'. Most abuses of the pattern fall into the obvious category of being nothing more than global variables by another name, but a great many abuses relate to coincidence: only a single object happens to be needed. For an example of this kind of misuse we need look no further than Design Patterns itself.
The Gang of Four's description of the State pattern suggests that Singleton can be used to implement classes that represent individual state behaviour, but which are otherwise stateless (a terminology collision that gives rise to the intriguing concept of stateless states). If an object has no associated state, and its behaviour but not its identity is all that matters, it makes little difference to code whether there is one instance or many. Each instance can be substituted transparently for any other, hence why we might favour having just a single instance for all uses. However, note that this is not Singleton: there is no requirement on the type that 'there can be only one', and hence no need to restructure the type so that it prevents public constructability, offers a global point of access, and so on. Instead, we have a possibility to reduce the number of instances based on 'there may be only one'. In other words, the code that wants to share the instance can simply declare a static variable of the stateless type, leaving the stateless type untouched and free of all the unnecessary Singleton coding clutter.
In the interests of fairness it is worth saying that I fell into precisely the same trap a number of years ago. Following a crisp and prescient description of Singleton's actual applicability, along with a spirited denouncement of its widespread misapplication, I then wrote the following [Henney1997]:
Another suitable application of SINGLETON is as an optimisation in cases where the identity of an object is not an issue, and there is no variation across instances of a class.
Publish and be damned! Redemption can be found in the draft for the unpublished article:
The important point here is that SINGLETON is really a creational pattern, which means that the main characteristic being constrained and controlled is creation, not access. Most of the few good uses of SINGLETON should, therefore, be invisible.
Such invisibility is achieved by decoupling usage of the Singleton instance from its access and creation. Instead of using the sole instance globally via the Singleton's class name, the class should implement an interface and the instance should be passed around according to that interface. In other words, not according to its concrete implementation type or the absolute path to the actual instance. In this sense, the locality and use of any Singleton becomes just like the locality and use of any other loosely coupled use of a factory. The incomplete, unpublished description of Parameterize from Above includes the following paragraph:
Create the object at the highest level it is needed and known and pass it down from there. This stresses the importance of having a clear layered structure. On close inspection most global concepts turn out to be regional rather than global.
In essence what has been described here is little more than a classic separation of concerns: separate how an object is used from how it is created. Such advice is both common and unsurprising and, put in such simple terms, it is also advice that is quite easy to follow. Furthermore, whether or not an object is actually a Singleton pretty much ceases to be an issue because you are programming to an interface, not an instance.
Resistance is useful
The view that Singleton is more of a problem than a solution has become increasingly widespread to the point that steering clear of it is considered to be common knowledge by many development communities. Where once the conspicuous use of Singleton was considered a sign of patterns know how and object-oriented design expertise, nearly a decade and a half after its publication it is increasingly seen as an obsession of the larval stage of pattern learning.
However, simply making Singleton a pariah without exploring the problem at hand or showing reasoned alternatives does not constitute constructive advice. The absence of a reasoned guideline can lead to the adoption of alternatives that solve superficial rather than deep issues. One such approach is the Monostate pattern [Henney2005]:
This pattern can be considered a salve for programmers who dislike the guilt-by-association of employing SINGLETON. It also plays the role of syntax sugaring for those who want a less cumbersome usage syntax than SINGLETON’s. A MONOSTATE [Ball+1997] object looks like an ordinary object but shares its state static-ally with all other instances of the class, leading to ‘spooky action at a distance’ and aliasing problems when the state changes. If SINGLETON is the problem, MONOSTATE as a treatment can be worse than the problem, although some developers mistake it for a cure. MONOSTATE is also known affectionately and revealingly as the BORG pattern.
It is true that with Singleton 'the programmer is obliged to use counter-intuitive syntax to access objects' [Ball+1997], but that ugliness should be taken as a hint. It is not the problem to be solved; it is the signpost to a deeper problem. Likewise, the use of the name Borg [Martelli2001] for Monostate in the Python community seems more like an early warning signal rather than an invitation.
A Monostate object is not always a drop-in replacement for a Singleton, but in many of its applications it is seen as comparable or equivalent (with the added bonus of absolving guilt and syntax). From a developmental point of view it also has some significant drawbacks [Ball+1997]:
- The sharing that is occurring may be overly subtle since all instances of a MONOSTATE class may appear to be unique.
- The subtlety of sharing can lead to aliasing problems, e.g., calling mutators on one instance of a MONOSTATE object will update all instances. This can cause subtle bugs if programmers don’t understand that all instances are aliases.
And I would contend that even though this list is briefer than it should be, it is damning enough, especially when the implications are considered more deeply - testing, code evolution, threading, etc. It is difficult to recommend a technique that is subtle and surprising, indiscreetly messing with the fundamental notion that different objects represent different objects, when the simpler alternative is to pass objects around as arguments - in plain sight and without the need for covert semantics.
One of the things about patterns is that they are recurring (and, of course, not all that recurs is necessarily good). This doesn't just mean that you read about patterns in books, hear about them at conferences or see them in other people's code. It also means that they are reinvented and rediscovered by individuals on a regular basis. A long time ago, in the land before GoF, I ended up solving an instance management issue by creating a design that I later recognised as Monostate. Without going into details, what I can say with the benefit of hindsight is that I wish I had known about Singleton: I may not be particularly fond of it, but it would have been a major improvement. To paraphrase Dorothy Parker, Monostate is not a pattern to be tossed aside lightly; it should be thrown with great force.
[Deigh2007] Teedy Deigh, 'A Practical form of OO Layering', Overload 78, April 2007, http://accu.org/index.php/journals/1327
[Henney1997] Kevlin Henney, 'Java Patterns and Implementations', BCS OOPS Patterns Day, October 1997,http://www.two-sdg.demon.co.uk/curbralan/papers/JavaPatternsAndImplementations.html
[Henney2003] Kevlin Henney, 'One or Many?' is the English original used for translation and publication in German as 'Eins oder Viele?', JavaSpektrum, September 2003, http://www.two-sdg.demon.co.uk/curbralan/papers/javaspektrum/OneOrMany.pdf
[Henney2005] Kevlin Henney, 'Context Encapsulation', EuroPLoP 2005, July 2005, http://www.two-sdg.demon.co.uk/curbralan/papers/europlop/ContextEncapsulation.pdf
[Henney2007a] Kevlin Henney, 'The PfA Papers: From the Top', Overload 80, August 2007, http://accu.org/index.php/journals/1411
[Henney2007b] Kevlin Henney, 'The PfA Papers: The Clean Dozen', Overload 81, October 2007, http://accu.org/index.php/journals/1420
[Henney2007c] Kevlin Henney, 'The PfA Papers: Context Matters', Overload 82, December 2007, http://accu.org/index.php/journals/1432
[Martelli2001] Alex Martelli, 'Singleton? We don't need no stinkin' singleton: the Borg design pattern', http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531
Overload Journal #83 - Feb 2008 + Design of applications and programs
|Browse in :||
All > Topics > Design (173)
Any of these categories - All of these categories