After creating my post about the Singleton pattern I noticed a lot of hype around this pattern and its use (or misuse) over the internet. I also found some very interesting and sometimes amusing posts/comments on the matter (which I've included at the end of this post). Interestingly there was a lot of negative content about this pattern and the side effects that it has on your design. Intrigued by all of this information I decided to write my version of why & when you should use the Singleton pattern and also try to answer a lot of misconceptions that I read about during the past two weeks. So here goes:
The original thought behind the conception of the singleton pattern is to provide an object oriented approach to alleviate the pain of common coupling (global data) and provide a different strategy when dealing with global data. Also the pattern was intended as a solution when you absolutely needed to limit the number of objects of a certain type (class). Note that the singleton pattern will not totally remove common coupling from your design and to solve the problem completely, in most cases, requires the designer to completely re-think their design. Let's understand common coupling and it's evils before we delve any deeper:
Common coupling is a situation where function/component/class/module A & B are dependent on one another (coupled together) through the existence of some sort of external (and shared) data area between them in which one can change the content and the other will read from it (or both will read and change). Now the two coupled entities might be calling each other or might not be using one another directly at all. It doesn't matter; they are coupled to each other due to their shared use of the common data area. The perfect example being global data shared between multiple functions/components/classes/modules etc. (just for the sake of brevity I'm going to refer to all these different sections of code that coupling might relate to as components from now on).
Why is common coupling or global data bad? Well let's start by answering this question: why do we want loose coupling between our components (as opposed to tight coupling)?
- When multiple components are tightly coupled we lose the chance for component reuse: If we want to reuse component A we must also take component B for the ride, because component B changes the global data and if the global data doesn't contain a certain state we will not be able to get the functionality we need from component A.
- Due to tightly coupled components we increase our cost of maintenance and tests. When something changes instead of only needing a replacement of component A's implementation (which relates to the global area), since other components are tightly coupled to it we need to change them as well. Once we change component A and test it we must also test the other components tightly coupled with it.
- When there is too much coupling we reduce the probability of independent design, coding & testing therefore creating a tougher environment as far as technical & project management is concerned.
- Too many tightly coupled components mean a bigger thing to understand: more complexity.
As we can see common coupling (which is a level of tight coupling) can become a major reuse hindering factor, especially in cases where future change and evolution of the system is expected. How many times have we all designed something using global data only to find out in later versions that the data that we assumed (wrongly assumed!) to only have one running instance in the system needed to actually have multiple running instances. For example suppose we were to design a word processor similar to WordPad that ships with Windows. One of the immediate design decisions that come to mind when designing this word processor is to keep the current document information in a global variable where all the different routines of our program can access it and so we don't have to pass it around (common coupling). Making this design decision will cause many problems down the road but let's just examine two:
- Components that we design (i.e. the spell checker) all depend on the existence of a globally available document to do their work and their reuse is hindered by this fact when we need to make use of them in a different project. As an example suppose we need a spell checker to check product information before it was saved to the database. If I had had the foresight to design my spell checker as an independent component that accepts what it's supposed to check as input (a rather abstract form of input but never the less an input not a global data area) and performed the checks on the input (as opposed to a global data area), I wouldn't have had this problem when the time came for its reuse.
- If in the future the stakeholders decide that we are going to change our SDI (single document interface) word processor into a fully fledged MDI (multiple document interface) application where the user can open and work on multiple documents simultaneously we are going to be in trouble. Again we would need to rethink our single point of access because now we have multiple documents being loaded and it no longer makes sense to have one single document in memory where everyone will access and use.
Having said all this, one can't deny the fact that using global data in some cases is absolutely essential. But the scenarios where use of global data as a design decision is correct and invaluable are rare and will only occur once or twice in a moderately sized program. So the big problem is detecting when you are to allow a global shared piece of data and when not to. Chances are that in most cases you shouldn't allow it and we'll try to figure out when to do this and when not to, but before we get into that let's get back to the singleton pattern.
Based on the discussion so far the singleton pattern is useful when their exists enough reasons to share something as global data in your program, at that point instead of sharing it as global data (or a static member variable of a class) you should expose it as a singleton. This way you have more of a control over how that piece of global data will be accessed and used and in the future you can maintain it a lot easier (since singletons are regular objects instantiated from classes) by using OO techniques and other design patterns. You can also make sure that only one instance of that global object exists and no one (on purpose or through a mistake) can create a second instance which could have dire consequences (depending on your object's role in the system).
OK now we can go back to the definition of the singleton pattern and figure out when to use it:
The singleton design pattern is used in places where you want to ensure that only a single (or a controlled number) of instances of a specific class exist. The client code using the object shouldn't be allowed to create new instances (since it doesn't make sense to create new instances) and all of the different areas of client codes should access that single object through a well defined access point.
As can be seen nowhere in the definition of the singleton pattern (in any documentation about the pattern) is there a reference to use a singleton whenever you feel like using global data! Or use a singleton whenever you're lazy and don't feel like thinking about your design so you just expose some object as a global object and let everyone in all the different layers of your software access it through the single point of access. Or any other similar excuse for singleton's (mis)use. In order to use a Singleton correctly one cannot break other design rules (such as Common Coupling) a singleton does not justify having common coupling in your design!
So where can we use a singleton? Well there are situations where hardware or some other fact from the domain or a non-functional (technical) requirement dictates the use of the singleton pattern. A perfect example is the polymorphic singleton that I've used in the Abstract Factory example so that we have one single point of access to our abstract factory and that single point is configured with one of its subclasses. The technical requirements of that design imply that throughout the execution of the software the singleton will only contain one object that never changes. Other such examples would be when you are managing one piece of hardware (i.e. a modem, printer, etc.) and even in this case it is always wise to think about the consequences of your design decisions if we end up supporting more than one of those hardware pieces (i.e. two modems at the same time). But even for places where we have a controlled number of hardware (multiple modems) we can use the multi-instance singleton (please read my post on the singleton pattern) to fix this problem.
Another perfect use of the singleton pattern is to make sure that from a specific class we only get one object per thread. An example is the Connection Manager (which in some cases we might need more than one per thread but for the time being we'll assume one per thread will do) in this case the singleton pattern is there to make sure that nobody creates an extra copy of the ConnectionManager and cause problems in the way we communicate with the DB but the singleton itself might have multiple instances (actually one for every active thread that is running) so code in each thread will get a different ConnectionManager object hence making sure that one thread wouldn't use another thread's database connection.
There are a lot of perfectly legitimate reasons to use a singleton and I've only touched on a couple of them here but to round things up I believe the following list will help make an informed decision. As with any engineering design decision you as the designer have to consider the tradeoffs and you will ultimately decide if the singleton will help you out in a specific scenario but you should also take care not to misuse it since its negative effects on your design (especially form a reuse point of view) can be disastrous:
- Is the candidate object a truly single object from a problem domain point of view? Will it always be that way (considering future growth of the software)? If yes by all means implement it as a singleton.
- Will the candidate object becoming a singleton cause the common coupling problem (as described above)? If so be very careful you might be better off not implementing this as a singleton. If it doesn't create a common coupling problem (as is the case with many technical requirements which point us to a singleton and won't create a common coupling problem) then a singleton would be a lot better than a global variable (or a static member variable).
- Is the data stored in the singleton object being used from multiple locations in your software and a change from one location will be picked up in another location? If this is the case go back and think really seriously about item #2 in this list, since it looks like your causing a common coupling problem. If it's not a common coupling problem but the question still holds true then ask yourself are the multiple locations that are going to be accessing this singleton located in different layers of the software possible deployed in different processes or across machine boundaries? Will they ever be deployed like that? A nice example of this is current user information. Suppose we are building a multi-layered distributed windows application. The UI layer authenticates the user and puts the current user credentials in a singleton object hoping that one of the back end layers (for example the DAL) will be able to pick it up and use it. In this case your code will work when you're testing it and everything is running in-process but will immediately break once you deploy it to the live environment where the UI resides on client machines and the DAL resides on a server. Why? Because singletons don't automatically get marshaled across process/machine boundaries. Actually that's the case for any global (static) variable and you would have to come up with some solution to get them across. In this kind of a scenario a singleton might work but you need extra effort and code to make sure the data it's holding gets marshaled across to the other process/machine.
- Does the problem domain consider a class to only have one instance, for simplicities sake or is that an immutable fact in the domain? For example suppose we're developing a financial accounting system and in this system we decide to create a singleton called OwnerCorporation: The OwnerCorporation is considered a singleton since the financial accounting system will be sold to one corporation and they will use it to keep a record of their financial records. But this decision (having only one OwnerCorporation object) has been made for simplicities sake it's really not true in the real world and the moment this app has to support other different scenarios in the real world this design decision will cause it to break. For example suppose down the line we decide to sell the app to an accounting firm where it will be used to track the financial accounts of multiple corporations hence needing multiple instances of the OwnerCorporation class. Or in the future we decide to sell our financial accounting services online and the same back-end routines need to be used to support multiple owner corporations each accessing the site to manipulate their specific information, again implementing the OwnerCorporation as a singleton will cause headaches in this scenario.
In general you should pay attention to the fact that some object might seem like a singleton from a certain view point but from a different viewpoint it's not a singleton at all (as in item #4 above). This is the case with many other design decisions that we have to make and the cost/time needed to design a perfect scenario that will fit all possible viewpoints is usually so prohibiting that most designers don't really bother. The same case is true for the singleton pattern BUT the issue that should be stressed time and again is that the singleton is not a valid replacement for good (or decent) design or we shouldn't use the singleton as an excuse to create global data shared by everyone and therefore creating common coupling in our system.
I know I promised to review some posts & comments that I found on the internet here but this post is very long as it is so I'll work on it in the next post.
No comments:
Post a Comment