【发布时间】:2019-02-27 20:12:17
【问题描述】:
我无法确定是否以正确的方式使用装饰器模式。假设我正在开发一个控制台应用程序。在这个应用程序中,我定义了一个简单的 IConfigPathProvider 接口,它将提供某个类的配置文件路径。
public interface IConfigPathProvider
{
string GetConfigPath();
}
路径存储在控制台应用程序的 app.config 文件的 appSettings 部分中。
public class AppSettingsConfigPathProvider : IConfigPathProvider
{
public string GetConfigPath()
{
return System.Configuration.ConfigurationManager.AppSettings["configPath"];
}
}
问题是这条路径也是加密的,所以...
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);
}
}
但是,等等:这还没有结束。我必须在路径中添加一个特定部分才能让它正确。
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));
}
}
现在 - 为什么不呢? - 让我们添加一些日志记录。
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;
}
}
分而治之
当然,即时结果是关注点分离,但是在实例化对象时增加的复杂性呢? 你需要知道哪个装饰器在前,它们应该以什么顺序被链接,否则你最终会得到一个有问题的 IConfigPathProvider。
new LoggingConfigPathProvider(
new AppendSectionConfigPathProvider(
new DecryptingConfigPathProvider(
new AppSettingsConfigPathProvider(),
decrypter));
这只是一个简单的提供者。在一个相当复杂的应用程序中,您可能会遇到具有多个引用的多个组件……这很容易导致维护噩梦。现在,这是众所周知的缺点还是我只是以错误的方式使用了这种模式?
【问题讨论】:
-
我们的代码也有类似的情况,但是我们并没有想太多。就我而言,以错误的顺序连接装饰器是程序员的错误。此外,作为程序员,您需要指定正确的顺序,因为您最终要执行定义明确的操作序列。
-
您可能会考虑将构建器模式作为替代方案,恕我直言,在这种情况下,代码的可读性更好,效果更好。您还需要考虑它是否是动态的,即代码的不同部分可以添加不同的提供者来获取最终对象,在这种情况下装饰器更好。但看起来您正在使用该模式立即创建/构建对象。 Builder 模式应该更适合这种情况。
-
@AD.Net 实际上我不认为构建器模式最适合这种情况:1)唯一的可选/动态参数是日志记录,因此每个其他装饰器无论如何都必须被束缚。请记住,这只是一个相当简单的示例,在生产系统中我可能还有 10 多个强制装饰器(大而杂乱的构造器); 2)如果我决定使用构建器方法来提高创建对象的可读性,而不是增加构建器构造器的负担,我最终会增加歧义(我应该先调用哪个方法?稍后调用哪个?按什么顺序?)
标签: c# oop design-patterns decorator