【问题标题】:Decorator pattern mess装饰图案混乱
【发布时间】: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


【解决方案1】:

这是一个众所周知的缺点。 GoF 提到了装饰器模式的以下责任。

很多小对象。使用装饰器的设计通常会导致系统 由许多看起来相似的小物体组成。只是对象不同 它们相互联系的方式,而不是它们的阶级或价值 他们的变量。虽然这些系统很容易被那些谁定制 理解它们,它们可能很难学习和调试。

【讨论】:

    【解决方案2】:

    你不一定是正确的。与其立即装饰对象,不如保留某种 decoration shema、可验证的、惰性的,可以通过调用 @987654321 将其转换为需要的(最终的、可使用的)对象@。只是一个代码草图:obj.DecorateWith<Decorator1>().DecorateWith<Decorator2>().DecorateWith(() => new Decorator3(IContainer.Resolve<SomeWhatArgument> ...).Build()。它确实让事情变得更加困难,但是只要装饰是正确的方式,并且您的项目确实足够大,可以从如此高的抽象中受益,它就会解决您的问题。

    【讨论】:

      猜你喜欢
      • 2015-07-25
      • 2020-01-31
      • 1970-01-01
      • 1970-01-01
      • 2016-09-28
      • 2020-05-21
      • 1970-01-01
      • 2016-05-08
      • 2012-01-17
      相关资源
      最近更新 更多