Recently I was reading a lot of stuff about application design patterns: about DI, SL anti-pattern, AOP and much more. The reason for this - I want to come to a design compromise: loosely coupled, clean and easy to work with. DI seems ALMOST like a solution except one problem: cross-cutting and optional dependencies leading to constructor or property pollution. So I come with my own solution for this and I want to know what do you think of it.
Mark Seemann (the author of DI book and famous "SL is anti-patter" statement) in his book mentions a pattern called Ambient Context. Though he says he doesn't like it much, this pattern is still interesting: it's like old good singleton except it is scoped and provide default value so we don't have to check for null. It has one flaw - it doesn't and it can't know about it's scope and how to dispose itself.
So, why not to apply Service Locator here? It can solve problem of both scoping and disposing of an Ambient Context objects. Before you say it's anti-pattern: it is when you hide a contract. But in our case we hide OPTIONAL contract, so it's not so bad IMO.
Here some code to show what I mean:
public interface ILogger
{
void Log(String text);
}
public interface ISomeRepository
{
// skipped
}
public class NullLogger : ILogger
{
#region ILogger Members
public void Log(string text)
{
// do nothing
}
#endregion
}
public class LoggerContext
{
public static ILogger Current
{
get
{
if(ServiceLocator.Current == null)
{
return new NullLogger();
}
var instance = ServiceLocator.Current.GetInstance<ILogger>();
if (instance == null)
{
instance = new NullLogger();
}
return instance;
}
}
}
public class SomeService(ISomeRepository repository)
{
public void DoSomething()
{
LoggerContext.Current.Log("Log something");
}
}
Edit: I realize that asking not concrete question goes in conflict with stack overflow design. So I will mark as an answer a post best describing why this design is bad OR better giving a better solution (or maybe addition?). But do not suggest AOP, it's good, but it's not a solution when you really want to do something inside your code.
Edit 2: I added a check for ServiceLocator.Current is null. It's what I intent my code to do: to work with default settings when SL is not configured.
A problem with an ambient context as the one you propose, is that it makes testing much harder. For a couple of reasons:
ILogger
.All these problems can be solved by simply injecting
ILogger
instances into services that need it, instead of using a Ambient Context.And if many classes in your system depend on that
ILogger
abstraction, you should seriously ask your self whether you're logging too much.Also note that dependencies should hardly ever be optional.