I'm having a problem figuring out if I'm using the decorator pattern the right way. Let's suppose I'm working on a console application. In this application I have defined a simple IConfigPathProvider interface, which will provide a configuration file path to some class.
public interface IConfigPathProvider
{
string GetConfigPath();
}
The path is stored in the appSettings section of the console application's app.config file.
public class AppSettingsConfigPathProvider : IConfigPathProvider
{
public string GetConfigPath()
{
return System.Configuration.ConfigurationManager.AppSettings["configPath"];
}
}
The thing is this path is also encrypted, so...
public class DecryptingConfigPathProvider : IConfigPathProvider
{
private readonly IConfigPathProvider _provider;
private readonly IStringDecrypter _decrypter;
public DecryptingConfigPathProvider(IConfigPathProvider provider,
IStringDecrypter decrypter)
{
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
_decrypter = decrypter ?? throw new ArgumentNullException(nameof(decrypter));
}
public string GetConfigPath()
{
var path = _provider.GetConfigPath();
//decrypting method of IStringDecrypter interface
return _decrypter.DecryptString(path);
}
}
But, wait: it's not over. I have to add a specific portion to the path to get it right.
public class AppendSectionConfigPathProvider : IConfigPathProvider
{
private readonly IConfigPathProvider _provider;
public AppendSectionConfigPathProvider(IConfigPathProvider provider)
{
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
}
public string GetConfigPath()
{
var path = _provider.GetConfigPath();
return System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(path),
"section",
System.IO.Path.GetFileName(path));
}
}
And now - why not? - let's add some logging.
public class LoggingConfigPathProvider : IConfigPathProvider
{
private readonly static ILog _log =
LogManager.GetLogger(typeof(LoggingConfigPathProvider));
private readonly IConfigPathProvider _provider;
public LoggingConfigPathProvider(IConfigPathProvider provider)
{
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
}
public string GetConfigPath()
{
_log.Info("Getting config path...");
var path = _provider.GetConfigPath();
_log.Info("Config path retrieved successfully!");
return path;
}
}
Divide et impera
Of course the instant outcome is the separation of concerns, BUT what about the added complexity in instantiating the object? You need to know which decorator comes first and in which order they should be chained, otherwise you'll end up with a buggy IConfigPathProvider.
new LoggingConfigPathProvider(
new AppendSectionConfigPathProvider(
new DecryptingConfigPathProvider(
new AppSettingsConfigPathProvider(),
decrypter));
And this is just a simple provider. In a rather complex application you'd likely come across multiple components with multiple references...this could easily led to a maintenance nightmare. Now, is this a well-known drawback or I'm just using this pattern in the wrong way?