So here is the
problem. I have objects that I want to use in many different places. These
objects don't have much state, but I end up passing them around all over the
place just in case I want to use them. Now passing an object is nice because
you are saying, "Hey this method requires this object" and this
statement is very helpful to people reading the code, but you can get into
longs chains where you pass tons of objects into a method because
sub-objects/sub-methods might need those objects. Then it goes from being clear
to being confusing. So there are a couple patterns I could use. One is
singletons and another is a depenency injection framework, but I have something
that is a little in between that leans more towards the "fluent".
Here is the basics
on the usage of the Singleton pattern:
SingletonObject
obj = SingletonObject.getInstance();
obj.MyMethod();
Now I have seen a
decent amount of criticism that singletons cause testing problems and really
just conceal global state. So we should
keep those criticisms in mind.
So dependency
injection is becoming more and more popular because it makes testing easier. So
the usage pattern is like the following:
IObjectOfInterest
obj = DependencyInjectionFramework.Get<IObjectOfInterest>();
Obj.MyMethod();
The
DependencyInjectionFramework object is set up by configuration such that for X
interface you get Y object. For testing we do something like:
DependencyInjectionFramework.InjectStub(typeof(IObjectOfInterest),
new MyMock());
…
DependencyInjectionFramework.ResetDefault();
So given these two
very common patterns I have a kind of play on them that I have been using
frequently. Since I do web development it is geared for the web.
public class
RepositoryFor
{
private const string ObjectTypeKey = "ObjectTypeKey";
private static IObjectTypeRepository _objectTypeRepository;
public static IObjectTypeRepository MyObject
{
get
{
if (_objectTypeRepository!= null)
{
return _objectTypeRepository;
}
if (HttpContext.Current.Items.Contains(ObjectTypeKey))
{
return (IObjectTypeRepository)
HttpContext.Current.Items[ObjectTypeKey];
}
IObjectTypeRepository repository = new ObjectTypeRepository();
HttpContext.Current.Items[ObjectTypeKey] = repository;
return repository;
}
}
public static InjectMyObjectStub(IObjectTypeRepository stub)
{
_objectTypeRepository = stub;
}
public static RestoreDefaults()
{
_objectTypeRepository = null;
}
}
public static InjectMyObjectStub(IObjectTypeRepository stub)
{
_objectTypeRepository = stub;
}
public static RestoreDefaults()
{
_objectTypeRepository = null;
}
}
With the usage
pattern being:
RepositoryFor.MyObject.MyMethod();
And the unit testing
pattern being:
RepositoryFor.InjectMyObjectStub(new MyMock());
…
RepositoryFor.RestoreDefaults();
You
can see this pattern is a lot like the dependency injection pattern. The key
thing is the default constructor is defined by code and not by configuration.
You also have objects grouped by the static wrapper. In the example this is RepositoryFor but you could add others like BusinessFor, HandlerFor, and so on.
So a couple points
on this pattern.
- Thread singletons vs. statics
In a web server context you can use ThreadStatic, but I have seen some evidence
that it is better to store objects in the HttpContext.Items collection because a request can be transferred between
threads. So you can see here that the HttpContext.Items collection is for when the code is running as part of the
webserver and the static variable backing this is for a testing context. Note
that one nice piece of this is that it isolates the HttpContext.Items collection which is a collection that can easily be misused.
To ensure that the variable is only used in a testing context
you could use a preprocessor variable or do something like ensuring that HttpContext.Current == null in the setter.
- Do you want global access?
You
can make a valid argument that creating globally accessible objects is bad
architecture and that you are creatign a pattern that allows poor design to
flourish. So patterns can be used badly. So there is that… I think in most
cases this pattern can be helpful and make code clearer. I have mainly used
this for repositories and business logic objects.
- No private constructors…
The
nice thing about the Singleton pattern is that it has a private constructor.
This is nice because you are sure that only one can ever be created, but then
you have the testing problem. In this mechanism your constructors are not
private so you could have people make calls to these constructors. So this is
something you give up for testability.
- Code over Configuration
So
this uses code instead of configuration. This is because in practice you don't
want to change things that much so configuration doesn't make much sense.
Configuration is also problematic when people get out of sync. Keeping code in
sync tends to be easier because you always want 100% code sync. So while this
is less flexible, I argue that you don't really need so much flexibility.
Okay, so why do this instead of using a full blown DI framework? Well, it isn't much work and you can avoid adding a new tool that people have to learn. You can use code to define behavior instead of configuration (tastes may vary). Basically you can use this pattern to start using a DI style of doing things without taking the full plunge. Also, when you do decide to switch to a DI framework the transition will be very easy.
Okay, so why do this instead of using a full blown DI framework? Well, it isn't much work and you can avoid adding a new tool that people have to learn. You can use code to define behavior instead of configuration (tastes may vary). Basically you can use this pattern to start using a DI style of doing things without taking the full plunge. Also, when you do decide to switch to a DI framework the transition will be very easy.
No comments:
Post a Comment