Re: Singletons



Responding to Sasa...

The example is the GoF book is one. Suppose you have an application that must stream error messages to a log file from multiple contexts where errors might occur. It is critical that there be only one ErrorFile instance open to handle all those messages. But each context may be in a different subsystem and thinks it should initialize the error file when it is initialized.




Yes, but this could also be resolved with passing parameters around?



Possibly. But the more common alternative is to simply create a single instance in a situation where there is only one opportunity to create it (e.g., at startup). One then forms normal relationships that always lead to that instance. IOW, one modifies the context where instantiation occurs.


What does it mean "forms normal relationship that always lead to that instance" and "modifies the context where instantiation occurs"?

All OO collaborations are done by navigating relationship paths among objects (i.e., the associations in a UML Class Model). There are four basic ways to implement a relationship: embedding an object in the implementation of another object; employing a referential pointer; passing an object reference as a message argument; and using an RDB-style search of instances by explicit identifier. The first is used when only the container needs access to the object. The second is the most common (e.g., it is the default for languages like Java). The third is used for temporary relationships. The last is very rarely used in OO applications.

Relationships are always implemented and navigated when addressing collaboration messages. Here the relationships that are relevant to solving the problem will naturally end up with all paths going to the same object instance because only one is created and the relationships are instantiated after that. This is very basic OOA/D. I would suggest getting any standard text on OOA/D for a detailed description.

By modifying the context I meant that one defines the solution flow of control differently so that the instantiation can be done in one place rather than in several (or within an iteration). Thus one moves the context within the solution for where one needs to instantiate the object. In one of the examples, that means moving the instantiation of ErrorFile from multiple subsystems to a single location in main().


What if in the future for some reason you actually want to change that, and have different contexts trace to different files? Wouldn't it be fairly difficult to refactor the singleton based solution?



Probably not. Singleton is really just a specialized factory object. The clients effectively ask it to create an object and it returns the singleton reference _as if_ it had been newly created. So if you need more instances you substitute a different factory object.


Singleton to me is not a factory. It is the one and only instance of the object. The GetInstance() returns reference to a single instance of the object. It may create it on the first access in case of lazy initialization, but its purpose is to retrieve it.

When the client is invoking GetInstance, what is it doing? It is creating an instance from its perspective. The fact that Singleton only actually creates an instance once is not relevant. It is still acting as a special kind of factory object.

Suppose the object being created is a subclass and each sibling subclass should only be instantiated once. One could modify Singleton trivially to do that where a parameter tells it which subclass to create. It creates one if there isn't any; otherwise it just returns the existing instance. Now Singleton is actually creating multiple objects, just like a factory object. Thus there is no substantive difference between Singleton and a factory object from the client's perspective.

IOW, the only difference between Singleton and a factory object is in the actual number of instances it creates _in its implementation_. There is no way for the client to know whether the instance returned is one of one or one of many. Think of it this way: for the first invocation of Singleton or a factory object, what difference is there?

So while I can agree that substituting with different factory might work with tweaking, I don't find it very elegant.

I think it is quite elegant from the perspective of maintainability. The change is entirely encapsulated and completely transparent to any existing clients. All one is doing is changing the /implementation/ of the Singleton object.

That problem can actually be solved without Singleton but there are other contexts that are more difficult. For example, in a networking situation one may need to send multiple message packets for a single message. Typically one wants to open/close one channel around the message and use it for each message packet. If the most common mode is for single packet messages, one might try to open a channel at the packet level. Singleton then provides a performance optimization by simply returning the existing instance handle when there is more than one packet.




I don't understand - why wouldn't one always open the channel at the message level?



Because many network protocols limit the size of the message so it has to be split up into separate packets. Those packets will be sent rapidly so one can avoid the overhead of channel allocation if one sends them all over the same channel. Also, depending on the configuration, using the same channel may make it easier to keep track of what packets need to be reassembled into a single message on the other end.


I still don't understand, why can't you open the channel at the message level. The packets are parts of message, hence all packets will use the same channel.

That is exactly my point. One does not want to open/close channels around packets. One wants to do it at the level of messages. But breaking up a message into packets may be done in a different place (maybe even a different subsystem) that doesn't understand channels. So one has to deal with channels in the context where they are relevant, which is dispatching packets to the network.

There is an analogy to database transactions. In the UI of the application that uses the DB, there is no need to know anything about DB transactions. However, the developer always finds some mapping that is relevant (e.g., the user hitting Save in a particular window triggers the close of an open DB transaction). The UI subsystem simply announces that the Save button has been hit. In the subsystem that understands RDBs that announcement is mapped into a closeTransaction action of some sort.

The same thing applies to messages. A separate subsystem that breaks up messages into packets knows when the past packet has been extracted and it can announce that with a lastPacket message. Whoever loads packets onto channels knows that the lastPacket announcement means it can delete the Singleton.


Similar situations arise when resolving nonfunctional requirements using patterns like Strategy. One only needs a single instance of each of the Strategy subclasses regardless of the number of Context clients.




Doesn't that make sense only if strategy is stateless?



Yes. Strategy should be stateless in most situations. It is a delegation of behaviors. It gets its data from the Context object.


What if Strategy subclass uses member variables to keep its internal state which is irrelevant to the outer world (some accumulating data etc)?

Then you would need to have multiple instantiations. My point is just that that is rather unusual. The much more common situation is something like an Employee object where one needs different strategies for computing the benefits in a payroll system. Each strategy will eat exactly the same data, which is all contained in Employee.

they do exist, they may not require the Strategy role in certain situations or may not require all of the subclasses. If the Context objects are created in quite different contexts within the application one has a problem deciding whether one needs to actually instantiate the Strategy tree elements or not.




Well, I usually instantiate specific strategy every time someone needs it. I do it through some isolated function so I have the chance of optimizing if necessary. Singleton could be the optimization in case of pure stateless concrete strategy classes, but even the I wouldn't make those classes as singleton, but rather have their instantiator holding the single instance to them (obtained via constructor and not via static GetInstance method).



In many situations instantiating a [Strategy] object every time it is needed would be inefficient because of the overhead of heap operations.


And in many situations it is no issue at all.
I do think though, that instantiating it always is simpler approach of the two and would devise some way of resolving performance issues only when they arise.

You don't do a lot of R-T/E, do you? B-)) In that world counting cycles is often critical.

However, from an aesthetic viewpoint I would argue that professionalism requires at least evaluating the alternatives. If it is too much trouble for the benefit, then fine. In the case of Strategy (when one doesn't /need/ multiple instances), though, I would bet that in most cases the total number of executable statements will be the same or fewer if one does only one instance. The performance gain comes from /where/ the statements are, not changing them. So there is no justification for not doing it right to improve performance (however small the gain may be).

<aside>
We have a generation of developers today that has lived with Moore's Law so long that they believe all performance problems are solved by getting a bigger and better computer. As a result they don't even know how to think about performance issues. Even Moore doesn't expect that Law to hold forever and when it hits an inflection point there is going to be a lot of gnashing of teeth as developers re-learn the hard-won lessons of the past.

In fact, most performance optimizations at the 3GL level are mostly a matter of proper style and mindset. IOW, there are almost idiomatic. Usually it doesn't take significantly more time and effort to optimize. It is only at the level of OOD where one makes strategic decisions (e.g., employing a write cache) where improving performance can lead to substantially more complex code. Then one has a valid trade-off vs. performance. And even then one can often implement so that the solution is reusable.

Rather than running out and buying a new computer every year people should start to ask why the Visicalc spreadsheet that ran on a TRS80 with 100 Kb of memory, floppy disks, and a clock rate measured in kilocycles can't possible work today without megabytes of memory, gigabytes of hard disk, and a gHz machine. Most of the problem is feature bloat, exotic UIs, and OS bloat, but a significant part is still due to the failure of developers to think about performance, much less write high performance code routinely.
</aside>


FWIW, IME the most common reason for using Singleton arises from dealing with non-functional requirements like performance. While there are customer problem spaces where the one-instance requirement is quite explicit, they tend to be pretty rare or they are trivial to enforce in the normal flow of control (i.e., Singleton never becomes an issue in the natural solution).


Thank you, I am of a similar oppinion. It might make sense that some class has one instance per defined context, however future requirements might call for different contexts to use different instances. I feel that simply ensuring that the context uses one instance (but not with singleton) is more flexible approach.

Still, the sentence from your original post in this thread puzzles me.

"Singleton enforces a business rule that there can only be one instance of a problem space entity regardless of how many opportunities there may be for instantiation at run time."

Isn't this contradiction, or am I missunderstand something?

It depends on the problem space. B-) When one resolves nonfunctional requirements the problem space is usually the computing space itself rather than the customer's problem space. We /know/ that heap operations are relatively expensive in that domain. So rules like minimizing heap operations become business rules of the computing space itself.

The question still remains - when does one need Singleton?

I think one needs it when:

(A) there must be only one instance AND

(B) there is no feasible way in the solution to reduce the number of opportunities to instantiate an instance to 1. Specifically, there is no reasonable way to avoid:

(B1) Instantiating the object in different parts of the solution and/or

(B2) Instantiating the object within an iteration of some kind.


*************
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
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH



.



Relevant Pages

  • Re: Singletons
    ... But each context may be in a different subsystem and thinks it should initialize the error file when it is initialized. ... Singleton is really just a specialized factory object. ... If the most common mode is for single packet messages, one might try to open a channel at the packet level. ... Because many network protocols limit the size of the message so it has to be split up into separate packets. ...
    (comp.object)
  • Re: Singletons
    ... By modifying the context I meant that one defines the solution flow of control differently so that the instantiation can be done in one place rather than in several. ... Singleton is really just a specialized factory object. ... There is no way for the client to know whether the instance returned is one of one or one of many. ... Because many network protocols limit the size of the message so it has to be split up into separate packets. ...
    (comp.object)
  • Re: Singletons
    ... But each context may be in a different subsystem and thinks it should initialize the error file when it is initialized. ... Singleton is really just a specialized factory object. ... If the most common mode is for single packet messages, one might try to open a channel at the packet level. ... Because many network protocols limit the size of the message so it has to be split up into separate packets. ...
    (comp.object)
  • Re: Singletons
    ... But each context may be in a different subsystem and thinks it should initialize the error file when it is initialized. ... Singleton is really just a specialized factory object. ... If the most common mode is for single packet messages, one might try to open a channel at the packet level. ... Because many network protocols limit the size of the message so it has to be split up into separate packets. ...
    (comp.object)
  • Re: Problem with matching kind of NDIS driver.
    ... When it comes to outgoing packets, the reasons why his task is infeasible ... context of different threads. ... NDIS IM filter that is located immediately below ... We are telling you that in a NDIS Intermediate driver the process ...
    (microsoft.public.development.device.drivers)