Unity Application Block and Decorator Pattern
by David Hayden
Recently I discussed the Decorator Pattern as well as Array Injection using Unity and I thought I would put concepts from both of these together to clean up the code I had written earlier with regards to filtering messages being logged to a ConsoleLogger.
Simplify Responsibilities
One could argue that the ConsoleLogger created in the previous post has too many responsibilities. It is both in charge of filtering messages as well as logging to the console, which can be a violation of the Single-Reponsibility Principle ( SRP ). We also can’t re-use the filtering logic with a DatabaseLogger, for example, without duplicating the filtering logic from logger to logger. This could lead us into violation of the DRY Principle.
public class ConsoleLogger : ILogger
{
private readonly ILogFilter[] _filters;
public ConsoleLogger(ILogFilter[] filters)
{
_filters = filters;
}
public void Log(LogMessage message)
{
foreach(var filter in _filters)
if (!filter.CanLog(message))
return;
Console.WriteLine(message.Message);
}
}
A better approach would be to split the filtering and logging logic between 2 classes, perhaps a FilteringLoggerDecorator and a ConsoleLogger. Let’s get the ConsoleLogger back to simple logging:
public class ConsoleLogger : ILogger
{
public void Log(LogMessage message)
{
Console.WriteLine(message.Message);
}
}
and then put the filtering in the FilteringLoggerDecorator:
public class FilteringLoggerDecorator : ILogger
{
private readonly ILogFilter[] _filters;
private readonly ILogger _inner;
public FilteringLoggerDecorator(ILogFilter[] filters, ILogger inner)
{
_filters = filters;
_inner = inner;
}
public void Log(LogMessage message)
{
foreach (var filter in _filters)
if (!filter.CanLog(message))
return;
_inner.Log(message);
}
}
Now with the responsibilities better separated, let’s add the new classes to the UnityContainer for use in an application.
Setting Up the UnityContainer
Like before, the filters will be registered with the UnityContainer as follows:
IUnityContainer container = new UnityContainer();
container.RegisterInstance<ILogFilter>(“CategoryFilter”, new CategoryFilter(new[] { “Security” }));
container.RegisterInstance<ILogFilter>(“PriorityFilter”, new PriorityFilter(3, 5));
The ConsoleLogger is then registered as an ILogger named instance ( not the default ). For brevity I didn’t specify a particular LifetimeManager as before.
container.RegisterType<ILogger, ConsoleLogger>(“Console Logger”);
The FilteringLoggerDecorator is registered as the default ILogger Instance. I specify in the registration that the ConsoleLogger is to be injected into the FilteringLoggerDecorator’s constructor along with the array of filters ( again not specifying a LifetimeManager ):
container.RegisterType<ILogger, FilteringLoggerDecorator>(new InjectionConstructor(typeof (ILogFilter[]),
new ResolvedParameter(
typeof (ILogger),
“Console Logger”)));
Now you can resolve the ILogger from the UnityContainer and experience the same results as in the previous tutorial. Only now the filtering and logging can change independently of one another allowing easier development and maintainenance.
var logger = container.Resolve<ILogger>();
logger.Log(new LogMessage { Category = “Security”, Priority = 3, Message = “This is a test.” });
Conclusion
Next time let’s remove the FilteringLoggingDecorator and replace it with a custom ICallHandler. We can then use the Interception Extension in Unity along with the new InterfaceInterceptor to achieve the filtering using AOP as an alternative to the Decorator Pattern. For more information, you can check out other Unity Tutorials.
Hope this helps,
Recent Posts: