| Home | Biography | Speaking | Articles | Books | Music |
The title of this post may sound a little odd, but here’s the situation we’ve run into. We’re incorporating the singleton pattern in our application to manage file watching. It’s actually a nice, slick design - the end result is we’re doing something like this:
SpecificData data = SpecificData.Instance;
This works well because the file that SpecificData watches is named in a configuration file. But this pattern is limiting because we can’t pass in the name of the file. Instance is a property so we can’t define arguments. Even if we change it to a method like GetInstance(), it still won’t work because the structure of our implementation requires the same signature.
There’s a way around this - I’ll generalize it using the creator pattern:
public static class Loader
{
public static ILoaded Load()
{
return new Load();
}
}
It’s a simple implementation, but it shows the issue. Load() returns an implementation of ILoaded:
public interface ILoaded
{
string Unload();
}
But the creational method takes no arguments. Therefore, we can’t get any information to Load:
public sealed class Load : ILoaded
{
private string stuff;
public Load()
: base()
{
}
public string Unload()
{
return this.stuff;
}
}
What happens if we want to get stuff set to a value, other than by reading a configuration file? There’s a way to do it via CallContext and defining a class that implements ILogicalThreadAffinative:
[Serializable]
public sealed class ExtraStuff : ILogicalThreadAffinative
{
public const string CallContextId = "ExtraStuff";
private string stuff;
private ExtraStuff() : base()
{
}
public ExtraStuff(string stuff)
: this()
{
this.stuff = stuff;
}
public string Stuff
{
get
{
return this.stuff;
}
}
}
We can now check to see if CallContext has an ExtraStuff instance and use that:
public sealed class Load : ILoaded
{
private string stuff;
public Load()
: base()
{
object extra = CallContext.GetData(ExtraStuff.CallContextId);
if(extra != null && extra is ExtraStuff)
{
this.stuff = (extra as ExtraStuff).Stuff;
}
}
public string Unload()
{
return this.stuff;
}
}
Of course, the caller has to get that set for us - here’s a test method to illustrate this:
private const string Stuff = "window";
[TestMethod]
public void LoadWithCallContext()
{
CallContext.SetData(ExtraStuff.CallContextId,
new ExtraStuff(LoadTests.Stuff));
Assert.AreEqual(LoadTests.Stuff, Loader.Load().Unload());
}
This also works in multithreaded scenarios as this test method shows:
[TestMethod]
public void LoadWithCallContextAcrossThreads()
{
using(EventWaitHandle handle = new ManualResetEvent(false))
{
string result = null;
CallContext.SetData(ExtraStuff.CallContextId, new ExtraStuff(LoadTests.Stuff));
Assert.IsTrue(ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
{
result = Loader.Load().Unload();
handle.Set();
})));
Assert.IsTrue(handle.WaitOne(5000, true));
Assert.AreEqual(LoadTests.Stuff, result);
}
}
I’m not saying this is an ideal solution. If you can get the information into a method via an argument, do it. But in the case where you’re stuck behind a pattern, this gives you a way out.
Published: 05.09.2007 09:17:00 PM CST