装饰模式,通过继承或依赖注入?

Amb*_*dex 5 c# design-patterns decorator

现在我正在研究常见的设计模式,并且在很大程度上我理解了装饰模式的目的.但我没有得到的是,在装饰器类中包装现有对象的目的是什么?

考虑这种情况,因为Progress是观察者模式的一部分,我想限制其订阅者的更新量以防止UI线程锁定.

所以我把类修饰为每50毫秒只更新一次.

public class ProgressThrottle<T> : Progress<T>
{
    private DateTime _time = DateTime.Now;
    public ProgressThrottle(Action<T> handler) : base(handler)
    {
    }

    protected override void OnReport(T value)
    {
        if (DateTime.Now.AddMilliseconds(50) < _time)
        {
            base.OnReport(value);
            _time = DateTime.Now;
        }
    }
}

public class ProgressThrottle2<T> : IProgress<T>
{
    private DateTime _time = DateTime.Now;

    private readonly IProgress<T> _wrapper;

    public ProgressThrottle2(IProgress<T> wrapper)
    {
        _wrapper = wrapper;
    }

    public void Report(T value)
    {
        if (DateTime.Now.AddMilliseconds(50) < _time)
        {
             _wrapper.Report(value);
            _time = DateTime.Now;

        }
    }
Run Code Online (Sandbox Code Playgroud)

这两个类完成相同的事情,除了我发现第一个版本更好,因为它允许我使用基础构造函数来设置进度更新的委托.基类已经支持覆盖该方法,那么我需要包装对象是什么?

这两个类都是装饰器模式的例子吗?我宁愿使用第一个选项,但我很少以这种方式看到例子.

Max*_*uez 4

想象一下您有n不同的接口实现IProgress<T>

\n\n

为了这个例子,让我们考虑两种实现:

\n\n
    \n
  • EndpointProgress<T>,这将轮询端点并且Report每次响应都不同。
  • \n
  • QueryProgress<T>,这将定期执行数据库查询,并且Report每次结果都不同。
  • \n
\n\n

为了使用第一种方法限制这两种实现,您必须创建 的两个实现ProgressThrottle<T>,一个继承自EndpointProgress<T>,另一个继承自QueryProgress<T>

\n\n

为了使用第二种方法来限制这两种实现,您只需使用EndpointProgress<T>和的包装实例QueryProgress<T>

\n\n
var throttledEndpointProgress = new ProgressThrottle2<int>(new EndpointProgress<T>());\nvar throttledQueryProgress = new ProgressThrottle2<int>(new QueryProgress<T>());\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

编辑:

\n\n
\n

因此,在我确信我不会多次扩展一个类来添加功能的情况下,不使用包装器是否可以接受?

\n
\n\n

我仍然会使用装饰器的第二个实现(我什至不确定第一个实现是否会被视为装饰器模式),原因如下:

\n\n
    \n
  • S.O.LID原则开放/封闭原则指出:

    \n\n
    \n

    软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭。

    \n
    \n\n

    如果您必须修改当前的Progress实现才能扩展它,那么您违反了开放/封闭。

  • \n
  • 继承ProgressThrottleProgress意味着每次Progress构造函数更改时,ProgressThrottle也需要更改其构造函数。

  • \n
  • 通过使用包装装饰器,您可以组合和组合装饰器。让我们考虑一个IProgress<T>记录每个onReport调用的实现。你可以根据配置、环境等 xe2x80x94 以不同的方式组合这些装饰器来实现不同的目标:

    \n\n
    var progress1 = new LoggingProgress<int>(\n    new ProgressThrottle<int>(new Progress<int>())\n);\nvar progress2 = new ProgressThrottle<int>(\n    new LoggingProgress<int>(new Progress<int>())\n);\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    此处,progress1将仅记录受限制的报告进度。progress2将记录所有报告的进度,但将以限制方式报告。根据您的目标,您可能需要一种实现或另一种实现;或者您可能需要两者,一个用于暂存中的诊断,另一个用于生产,但最重要的是您不必更改装饰器的实现来更改此行为。

  • \n
\n

  • 您不应该仅仅为了使用设计模式而使用它们。正如 Maximo 所解释的,装饰器允许静态或动态地向对象添加新功能,而不会影响同一类对象的行为。如果没有这样的需求,就没有必要使用装饰器模式。如果你想要一个“最佳实践”来解释为什么不使用它,只需考虑 YAGNI (https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it) (3认同)