Architect or Cobbler?
Good code starts with good design

The One and Only

Sunday, August 28, 2005
Over the next few weeks I'm going to investigate the GoF patterns, and show you the source code that you need to write to use them. I'll tackle them in no particular order, but I will look at all of them.

Probably one of the most common patterns you'll come accross is the singleton pattern, where you want to ensure you only have one object in an AppDomain. The easiest way to ensure this is quite simple:

For instance if you want to ensure there is only ever one King, you'll write something like this:


public class TheKing
{
static TheKing Elvis = new TheKing();

private TheKing()
{
// You don't want clients to create
// their own King instance
}


public static TheKing TheOneAndOnly()
{
return Elvis;
}
}


 

Which is probably OK for 90% of applications. But what if the King was an overweight bloated object, you may want to support lazy initialization. In this case you will have to acquire locks because multiple clients may request TheOneAndOnly at the same time, so your code may look something like this:



public class TheKing
{
static TheKing Elvis;

private TheKing() {}

public static TheKing TheOneAndOnly()
{
lock (typeof(TheKing))
{
if (null == Elvis)
Elvis = new TheKing();
return Elvis;
}
}
}


 

This is as safe as houses, but not particularly performant. Every client requiring access to TheOneAndOnly will aqcuire a lock, even though once the object is created it is not necessary. To fix this consider using a double locking pattern, like this.


public static TheKing TheOneAndOnly()
{
if (null == Elvis)
{
lock(typeof(TheKing))
{
if ( null==Elvis)
{ // double check
Elvis = new TheKing();
System.Threading.Thread.MemoryBarrier();
}
}
}
return Elvis;
}


 
You may have heard that the double locking pattern can't be relied on, as write ordering is not guaranteed. To a certain extent this is correct, however on the X86 architecture write ordering is guaranteed, so this is acceptable. On other architectures, or on multiple CPU systems you may want to force a synchronization by using the MemoryBarrier method. This code is safe on all architectures, for the small expense of a single extra synchronization.


You would think that that's it, however there is one more problem. Serialization may cause some problems. Mark TheKing as Serializable and run the following code.



class Program
{
[STAThread]
static void Main()
{
FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
try
{
// Construct a BinaryFormatter and use it
// to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
// Create an array with multiple elements refering to
// the one Singleton object.
TheKing[] a1 = { TheKing.TheOneAndOnly(), TheKing.TheOneAndOnly() };
// This displays "True".
Console.WriteLine(
"There is only one king " +
(a1[0] == a1[1]));

// Serialize the array elements.
formatter.Serialize(fs, a1);

// Deserialize the array elements.
fs.Position = 0;
TheKing[] a2 = (TheKing[]) formatter.Deserialize(fs);

// You'd expect this to print "True"
// but it displays "False".
Console.WriteLine(
"There is only one king? " +
(a2[0] == a1[0]));

Console.WriteLine("Press Any Key to Continue");
Console.ReadLine();
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
}
}


 

Clearly there are now two Elvises. Deserializing the object has allowed the subtle creation of a new Elvis object. To fix this you need to take charge of the Serialization and Deserialization process. Your code for your TheKing object will now look something like this



[Serializable]
// time to fix serializable bug

public class TheKing : ISerializable
{
static TheKing Elvis;
private TheKing() {}


public static TheKing TheOneAndOnly()
{
if (null == Elvis)
{
lock(typeof(TheKing))
{
if ( null==Elvis)
{ // double check
Elvis = new TheKing();
// On the X86 write orderings are guaranteed
// so the following is not needed
System.Threading.Thread.MemoryBarrier();
}
}
}
return Elvis;
}

// A method called when serializing a Singleton.
void ISerializable.GetObjectData(
SerializationInfo info, StreamingContext context)
{
// Instead of serializing this object,
// serialize an ElvisHelper instead.
info.SetType(typeof(ElvisHelper));
// No other values need to be added.
}
}

[Serializable]
internal sealed class ElvisHelper : IObjectReference
{
// This object has no fields (although it could).
// GetRealObject is called after this object is deserialized.
public Object GetRealObject(StreamingContext context)
{
// When deserialiing this object, return a reference to
// the King instead.
return TheKing.TheOneAndOnly();
}
}



 
So there you have it, a lazily initialized, thread safe Serializable Elvis. I bet you didn't think it would be that much work when you started reading the blog.

# posted by James @ 2:44 PM   0 comments

Tomorrow never comes

Friday, August 26, 2005
.. was a mediocre film in the Seventies, although it did have the gorgeous Susan George in it.

You stop blogging for a few days, and before you know it months have gone by. I blame one of my colleagues, who read my blog and started calling me "Geek Boy", but if the truth be known it's bone idleness that's the root cause of the malaise.

Anyway Dave was upbraiding me last night (at the pub again, for some strange reason) - so in a fit of contrition, I am resolving to start blogging again.

So check in over the next few days and Geek Boy should be fully back in action.

# posted by James @ 4:24 PM   1 comments
This page is powered by Blogger. Isn't yours?