Re: Can use of singletons denote poor project design?
- From: "H. S. Lahman" <h.lahman@xxxxxxxxxxx>
- Date: Sat, 25 Feb 2006 15:50:56 GMT
Responding to Phlip...
John Fly wrote:
I've found the use of singleton objects quite nice for various aspects
of this program, I do enjoy having the ability to use a singleton class
by using the Class::Instance() syntax, thus removing the need to pass
instances around in all my function calls. Also very useful is the
ability to use a guarenteed single instance by simply including the
header containing my singleton.
The most important design feature is testability. Classes must rigorously
decouple from each other to submit to test cases with very fine
granularity.
(You do _have_ test cases, don't you?)
I don't think using Singleton has anything to do with testing. Singleton is a response to a problem space constraint where there can only be one object created from a particular class AND in the normal course of processing there may be multiple opportunities to create such an object. Multiple opportunities may arise in iterative processing of the same creation code or by having separate creation code (or triggers of the creation code, such as invoking the same factory object from different solution contexts) for different contexts in the solution.
Note that if there must only be one instance but there is only one opportunity to create it (e.g., as part of startup initialization) then there is no need for the complexity of Singleton because there is no way to create additional objects in the normal course of execution. That is, the solution flow of control naturally enforces the rule.
Unfortunately Singleton tends to be overused. In particular there are two common abuses. The first is simply to introduce global data. Occasionally there will be problem space entities that have an intrinsic responsibility to manage state data for other entities, but those are pretty rare. More commonly the developer just takes the easy way out and creates a Singleton rather than properly allocating the responsibilities, properly instantiating relationships, and managing instance creation itself.
The second abuse is the one described by the OP where the developer is trying to avoid passing object references around. Avoiding passing object references is a very good idea, but Singleton is not the way to do that. Passing an object reference is essentially instantiating a temporary relationship that exists only for the duration of the call. That has two drawbacks. First, it requires the caller to understand the receiver's relationships. IOW, we have:
[Client]
| 1
|
| R1
|
| 1
[Service]
| 1
|
| R2
|
| 0..1
[OtherService]
Client navigates R1 to collaborate with Service by sending Service a message. Service, in turn, needs to collaborate with OtherService by navigating the R2 relationship with a message. When Client includes the OtherService reference in its message to Service, Client is essentially establishing the R2 relationship. That means Client must understand the rules and policies for relating [Service] to [OtherService] and typically that is none of Client's business. IOW, the R2 relationship is a personal matter between [Service] and [OtherService].
In addition, the rules and policies for instantiating /participation/ in relationships are quite often different than the rules and policies for collaboration between objects. IOW, relationship instantiation is about Who should collaborate but relationship navigation is about When to collaborate. Usually those disparate sets of rules and policies are encapsulated in different objects.
The second problem is that for temporary relationships at least one end of the relationship must be conditional. Conditional relationships lead to more complex code and additional overhead so one should avoid them whenever it is feasible. (Sometimes they can't be eliminated, but the reviewer is going to want a good reason why they can't.) That provides a clue as to how one avoids passing object references without using Singleton.
The proper solution lies in defining the relationships in a better fashion by doing things like eliminating conditionality. More important, though, are the basic OOA/D notions that (A) a behavior is self-contained so it doesn't directly depend on what other behaviors do and (B) that a behavior accesses the data it needs on an as-needed basis by navigating relationships paths _that it already has_. Thus when Service's method it invoked, it should navigate some existing relationship path to get what it needs from OtherService -- in this case the R2 relationship (e.g., via it own private myOtherService reference).
There is some problem context that prevails when Client sends its message to Service. That context will determine that Service will need to collaborate with a particular OtherService. So as soon as that context prevails the R2 relationship should be instantiated so that Service will get to the right OtherService. That will context will prevail /before/ Client collaborates with Service. (In fact, for Client to provide the OtherService reference that will already be true.) The developer just needs to back-track to to point where the context prevails and instantiate the R2 relationship at that point in time.
*************
There is nothing wrong with me that could
not be cured by a capful of Drano.
H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions -- Put MDA to Work
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
(888)OOA-PATH
.
- Follow-Ups:
- Prev by Date: Re: How to invert dependency?
- Next by Date: Re: Can use of singletons denote poor project design?
- Previous by thread: Re: Can use of singletons denote poor project design?
- Next by thread: Re: Can use of singletons denote poor project design?
- Index(es):
Relevant Pages
|