eyt*

Find your next scenic drive!

January 29, 2005

The Multiple Personalities of the Singleton

The Singletongof Design Pattern is by far one of most popular and most used design patterns, but the pattern is mostly associated with only one of its many uses, viz. the pattern’s intent, providing a single instance of a class from one global access point (and of course, the double check locking pattern, or a safe adaptation thereof, for multithreaded code).  In a recent discussion on a Patterns mailing list, the topic of destroying a Singleton object came up, and it is interesting various answers that came up during the thread.  This question is more involved that it first appears, and the problem is less about the mechanism of destroying the object, but instead surrounds the type of Singleton that we are talking about.

Unfortunately this problem is not really discussed in literature.  The only place that I am aware that really has a good modern treatment of the issue is Modern C++ Design, whereby Andrei says, “From a client’s standpoint, the Singleton object owns itself. There is no special client step for creating the singleton. Consequently, the Singleton object is responsible for creating and destroying itself. Managing a singleton’s lifetime causes the most implementation headaches.”

One of the approaches recommended in the thread of the aforementioned post was to simply do nothing. Baring in mind that in languages such as C++, Singletons are generally created on the heap, some may concern themselves about a memory leak, But as noted in Effective C++ Item 10, there is no memory leak in this approach, since to have a memory leak, you must lose a reference to memory, but this is not going to happen, and instead, most modern operating systems, memory utilized by an application is returned to the operating system when the application is terminated.

Instead, this approach has a worst problem, in that there is probably a resource leak. For example, a printer queue Singleton will likely have some communication link with a printer or other distributed printer queues or clients. By exiting without properly closing those connections, it is possible to have global leaks throughout your network. It is likely that that you will have to develop software to guard yourself against such problems in the unlikely case that a hardware or software problem occurs, but such problems should be exceptional and not the norm. In the cut and dry case of opening an application to display the contents of a print queue, you do not want such problems on your network.

If you are using a language like C++, one technique recommended in More Effective C++ Item 26 is what Andrei refers to as the Meyers Singleton, whereby you leave the languages do the construction only when it is required and let the language do the destruction at the end of the language.  An example of this technique follows:

Singleton& Singleton::getInstance() {
   static Singleton obj;
   return obj;
}

As explained in detail in the two aforementioned resources, the objects are destroyed in a LIFO manner, which is invoked by the little know function, atexit().

There are three unmistakable issues with this approach. First, dependencies on other Meyers Singletons can become problematic, as Andrei describes, since the order of construction and destruction can lead to undefined behaviour. The example that Andrei provides is the one where the Logger is only initialized when there is an error, and it emits an error on the Display. For the sake of this example, the Keyboard is instantiated first and then the Display, and during instantiation, the Display experiences an error, resulting in the Logger being instantiated. When you exit the application, these Singleton objects are destructed in the opposite order that they were constructed, thus the Logger is destructed first, then the Display, and finally the Keyboard, but for our example, let's say that the Keyboard experiences an error on destruction. What now? If the Keyboard accesses the Logger, it is now uses a Dead Reference that has been destroyed, which leads to undefined behaviour. As there is no way in C++ to define these types of dependencies amongst static variables, the solution that Andrei puts forth is to track the destruction of the object via a boolean variable, and therefore behave slightly more definably, but it is still not ideal. An alternative that Andrei describes in Modern C++ Design is what he refers to as the Phoenix Singleton, in which when it detects that it has died, the object is recreated using placement new, and destroyed again by using atexit() (with a few library implementation caveats here, since the behaviour is undefined for this case).

The second is that the type of singleton must be know a priori; for example, per se that your Singleton represents access to a configuration file. While today you may only be interested in a file on disk, it is possible that in the future you will have requirements that the data will be stored in a database, in another format (such as XML), or remotely using some middleware (such as CORBA). The above approach would require you to make this decision at design time, not runtime.

The other issue is with the lifetime. While we have delayed the construction of the object, the object destruction is still at the end of the application. Depending on the genre of Singleton, this could be a desired attributed, but it is important to distinguish the difference between the application’s lifetime and the Singleton’s.

In my last sentence, I may have struck a nerve with some, but it is important to note that the GOF book provides several consequences to choosing a Singleton design pattern. Most people are really comfortable with the first one, which is to provide access to a sole, unique instance. This means that during the lifetime of an application, any call to getInstance() returns the same object, at the discretion of the Singleton object.  In other words, the Singleton can decide how and when clients will use the object.

The second consequence is that it can clean up the global namespace; in particular you do not have global data or methods, and instead just have a simple interface to acquire an instance to the class. This may not be as important today as it was originally, with concepts like packages and better compiler support for namespaces (The C++ ISO Standards committee voted namespaces into the language in July 1993, per The Design and Evolution of C++).

The third consequence is that the Singleton pattern lends itself nicely to refinement of operations and representations. In other words, the client using a Singleton class is interested in performing a function; they are not particularly interested in how the class is implemented. For example, the Configuration File example I previously mentioned fits this nicely. The user is not particularly interested in whether it is a File, Database, or accesses the file via middleware. The import aspect is that it provides the functionality that the client uses and supports the contract set forth by the Singleton.

The fourth consequence is actually one that surprises many people, in that it states that the Singleton object can control the number of objects that are on a particular system. This can be used for many effects, such as returning a unique instance for each Thread in the system or something more like load balancing.

The fifth consequence essentially highlights the differences between the Singleton design pattern and the Monostate [PDF] design pattern, in which the Singleton pattern provides more flexibility in implementation than the Monostate pattern.

Now that we have gone through the five consequences, it should be clear that the Singleton design pattern’s purpose is not to provide access to one object, but rather to control access to objects, centralizing this control, such that you can easily modify the implementation’s policy for accessing objects in a single place, the Singleton itself. For example, the Pool pattern from POSA3 could easily be a specialization of the Singleton pattern.

Obviously, the delete Singleton::getInstance(); is not the solution; in addition to the implementation of the Singleton object changing, of equal concern is the fact that the memory location may not be allocated by new. It may have be allocated by a placement new or a memory manager, whereby the deletion of the object via delete would be inappropriate.

John Vlissides, one of the GOF, wrote some portions of this in his Pattern Hatching and, in particular, To Kill a Singleton. This is an excellent read on the topic, although it is partly dependent on C++. Never-the-less, the first technique that John discusses resembles the Meyers Singleton, In it, he recommends allowing the language to recoup the memory by creating a class that holds a Singleton and is responsible for destroying it.  Of course, just as the Meyers Singleton, this scheme does not work when there are Singleton dependencies.  Furthermore, it also only works when the lifetime of the Singleton is the same as the lifetime of the application, which John indicates is normally the case.  In the Odds and Ends section of the column, John notes that the static system employed in these schemes is not thread-safe; since C++ does not yet have knowledge of threads, multiple threads can access the same static initializer at the same time, and no locking scheme can prevent this from occurring (See also the double check locking pattern).

In response to the dependency issue, John simply points out that an atexit() scheme (similar to Andre’s) may be the only way around this, although the difference between Andre”s scheme and John”s is that John is recommending a single object that takes care of deleting all the Singleton objects in an application, and that this object maintains the dependency information as required.  David L. Levine, Christopher D. Gill, and Doug Schmidt formalized this into the Object Manager Pattern [PDF], which aims to resolve many of the issues that have been issued above, not to fail to mention the issue with certain embedded systems and static initialization.

So what is the proper answer to how to destroy a Singleton?  The answer really varies based on your use and system architecture.  In a large application, the Object Manager should be used or at least considered.  For smaller applications, this can depend on what the Singleton really is, and how important it is for it to be destroyed.  But there is definitely more than meets to eye.

December 15, 2004

Choosing the Price...

Joel on Software has a new piece entitled Camels and Rubber Duckies, which talks about selecting the price of software. Joel presents the entire science behind the price, and then tears it all apart.

He presents some interesting ideas in the discussion. When you say that your software is work a particular value, your customers will immediately compare you to your competition. For example, the UML modeler prices that I previously discussed are an amazing example of this. You have products that start at $100 USD all the way up to $5000 USD, and I completely agree with Joel when he says that choosing a cheaper product sometimes makes you think that you are getting a lesser product. Maybe you are. Maybe you aren't. The real difference is that the organizations that are selling it at $5000 have customers that are willing to purchase it at that price, but obviously students and individuals cannot afford these prices. Unfortunately, you cannot figure out how much people are willing to pay until you get it out there.

Although Joel does not present any real solid information, it is a great read, especially if you are looking at setting the price for something.

December 12, 2004

Static Generic Methods in Java...

I have been applying Java generics to my new code base, and thanks to it, I have already caught a few errors that I hadn't yet caught with inserting the wrong type into the container. Of course such errors would have been obviously caught by proper unit tests, but by using generics, I knew immediately the problem.

Earlier today, however, I was trying to sort a List, and using my C++-templates knowledge was not as intuitive as I thought it would have been, but this all just turned out to be my problem. The issue is that I had created a type, Type, that implemented the Comparable interface, but I had not made use of the generic type here, such as follows:

class Type implements Comparable {
 public Type() { /* ... */ }

 public int compareTo( Object rhs ) {
    if ( rhs instanceof Type ) {
      Type r = (Type)rhs;
      // Do the comparison
    } else {
      // Don't know what to do with this; make something up
    }
  }
}

The problem that I encountered though is when I went to sort a collection of this type. I had created a list of the type, so I was looking at using java.util.Collections's sort() method, so I pecked in the following code:

java.util.List<Type> list = new java.util.LinkedList<Type>();
// populate list
java.util.Collections.sort( list );

And by so doing, I received the ever-popular unchecked method invocation warning. My instinct was that it needed a hint about being Comparable, and I was surprised that the signature was not:

java.util.Collections<Type>.sort( list );

But rather:

java.util.Collections.<Type>sort( list );

This looks very awkward, to say the least, never-mind how I was thinking that it should infer this knowledge based on what I told it. But either way, it was still giving me that warning, and it was only when I started looking at the signature a bit closer that I realized my mistake.

The final version of the code is not unlike the original one after all, only much cleaner:

class Type implements Comparable<Type> {
 public Type() { /* ... */ }

 public int compareTo( Type rhs ) {
    // Do the comparison
 }

}

java.util.List<Type> list = new java.util.LinkedList<Type>();
// populate list
java.util.Collections.sort( list );

If you look at the actual changes in the code, the code is actually extremely cleaned up. The ugly case of figuring out what to do if compareTo is called on a type that you do not know anything about, for example, is completely removed, since it is no longer possible to be called this way.

This is exactly what I was thinking about when I was writing Java Generics: Better Code or Worse?, but I had not really come across such a solid example as this one. Most of the people who preach Java generics generally do so by showing you how you no longer need to cast types from Object in containers and the safety that this awards you. And while this is an extremely visible portion of generics, generic interfaces such as Comparable allow you to very clean code, as illustrated above, allowing you to focus on the types you care about and not the odd-ball cases.

December 8, 2004

Coding Standards...

JavaPro has a piece on Why Coding Standards?, and I could not agree more. Each developer having their own style really makes maintenance and reading code much harder, and one problem that generally comes out of this is that branches are merged that reformat the code to the next developers tastes, and they happen to fix some bug in the middle of the code. These types of branches are the worst, because it makes it near impossible to compare the latest version of code to an older versions, since this stylistic factor overtakes the code, and you cannot make heads or tails of the changes.

Of equal annoyance are those developers who redo the code without understanding what the code actually does. Just earlier today I was reminded of this, as I was shown a piece of code that the person purported was mine. I looked at it, and I was rather surprised that it was mine as I quickly noticed a few obvious mistakes in the code. Looking through the history of this particular file, I was able to track down that the same developer who was confused of the code was actually the one who had rewritten the original code for simply stylistic reasons. This cannot be confused with refactoring, since the code was changed without maintaining the original contracts.

Coding standards can help to prevent bugs. I always use and highly recommend the standard that the article discusses from Sun, where statements are always surrounded by brackets, has prevented many bugs, and sometimes some confusion, simply by using this style. The next one regarding commenting is not one that I fully subscribe to though, since this generally produces code that has no comments what so ever, and such code may make sense today, but in the future, it can be hard to actually understand what the algorithm or function is suppose to do. This particular article hints that JavaDocs could be used instead of this, which could work nicely, since your methods should be short.

In the area of coding standards, getting very strict can make all the code look as it were developed by a single developer. To do this, though, some help of tools generally help. Tools like Borland's JBuilder has some reformatting and analysis capabilities that allows you to check your code against the standards while you are writting it, and this can be very useful.

Choosing the right standards, however, can be involved, and this is not covered by the article. For Java, many developers follow the Sun Coding Convension. For C++, this is hard one; from what I have seen, this may be C++ Coding Standards by Herb Sutter and Andre Alexandrescu, but there are really many topics and things to consider in such standards. Definitely some common knowledge, such as that of Effective Java, Effective C++, More Effective C++, Effective STL, Large Scale c++, and a variety of other books should be part of that coding standard.

Standards simply make better code, but you cannot apply such standards blindly; you need to understand why you are applying the standard, and more specifically, you need to know when you can break the rules. If you do not have a coding standard, it is probably time you have one.

November 23, 2004

Got your back...

Peter Varhol has posted a blog entitled Got Your Back, and in it, he talks about ways to ensure stable code, by essentially knowing how to use tools. His examples note code reviews, unit tests, and exception handling in the list of tools, but I would also add in logging in this list, since when you log sufficient information, it can be used to also see situations and bugs that would be extremely harder using other methods.

All of these tools are essentially insurance for your software, which could contain anything from a simple one line bug or a complex architectural problem. The aforementioned tools will attempt to exercise your software in ways to expose these conditions. Of course, this means that you really have to include code reviews in your process, and to actually attempt to break code when creating test suites and run such automated tests regularly. This means that you have to use language features to make your productivity higher, and that you need to actually log information in an accessible and usable fashion.

At present, you need people to ensure that these things happen, and this comes back to the broken window theory. If a developer is used to strict coding standards, and is then exposed to a group with no coding standard, chances are that his own code will get slightly sloppier, simply out of the case that there is no authority to curve this. But such examples are an over abundance in business, as discussed in books such as Database Systems Management and Design.

There is, however, no question that such behaviours are time consuming, and as such, it would be great to automate these to take some burden off the people involved in a project, but as Peter says, we are not there yet, and I think we are far off. Modern IDE's have been improving significantly over the years, and movements into this direction are highlighted by Borland's integration of their Caliber requirements management application into their IDE's. And while this does seem that it would allow for tracking of software features from inception to delivery, I suspect that such products will suffer from how they are used and maintaining that ever important shared vision. Specifically, if the tools are not properly integrated, I see developers using them in vastly different ways, and while this flexibility is good for some aspects, this flexibility could harm the usability of feature tracking. But also such tools will still require human intervention for many years to come, as they will not really be able to decide the completeness of a feature alone, let alone whether the implementation accurately reflects the desires of the user.

Until we are presented with software that can think for itself, there is no way to get rid of people completely, but by using unit tests, language features, and logging coupled with code reviews, quality software can be not only developed, but maintained.

Earlier Entries

<  1  2  3  4  5  6  7  8  9  10  >

Navigation

Recent Posts

eyt*