【问题标题】:What can be done to fix a decorator whose root class requires the decorator instance?可以做些什么来修复根类需要装饰器实例的装饰器?
【发布时间】:2013-02-12 11:31:20
【问题描述】:

我正在重构一个软件,试图使其更易于测试/DI 友好,并使其更具可扩展性。原始代码依赖于继承,但我认为装饰器会是一种更灵活的设计,因为我希望组件的最终用户能够在我正在创建的某些层下方插入层。

但是,由于基类中的某些代码将this 传递给某些方法,因此我遇到了麻烦。使用继承这不会是一个问题,因为this 将引用顶层类型,但我在尝试弄清楚如何使用装饰器使其工作时遇到了麻烦。这是一个例子:

public interface INode
{
    bool IsReadOnly { get; }
    void DoSomething();
}

public class Node : INode
{
    public Node(ISomeFactory someFactory)
    {
        if (someFactory == null)
            throw new ArgumentNullException("someFactory");
        this.someFactory = someFactory;
    }

    private readonly ISomeFactory someFactory;


    public bool IsReadOnly { get { return false; } }

    public void DoSomething()
    {
        // Some implementation code here

        // This factory doesn't get an instance of the decorator type
        // when it is in use - this is a problem
        var someInstance = someFactory.Create(this);

        // More code here...
    }
}

public class LockableNode : INode
{
    public LockableNode(INode node, ILockingService lockingService)
    {
        if (node == null)
            throw new ArgumentNullException("node");
        if (lockingService == null)
            throw new ArgumentNullException("lockingService");

        this.innerNode = node;
        this.lockingService = lockingService
    }

    private readonly INode innerNode;
    private readonly ILockingService lockingService;

    public bool IsReadOnly { get { return lockingService.IsReadOnly; } }

    public void DoSomething()
    {
       if (this.IsReadOnly)
           throw new InvalidOperationException("Node is read-only");

       this.innerNode.DoSomething();
    }
}

然后我的工厂做这样的事情:

var someFactory = new SomeConcreteFactory();
var lockingService = new LockingService();

var node = new Node(someFactory);
var lockableNode = new LockableNode(node, lockingService);
return lockableNode;

我的评论中概述的问题是我试图装饰的代码中的某些地方,当前对象作为参数传递给其他方法,并且在使用时我需要装饰器对象的实例和当前对象不是。没有在装饰器类中重新实现将this 传递到工厂的代码,有什么办法可以解决这个问题吗?

【问题讨论】:

  • 假设基节点被多个装饰器装饰,那么应该将哪一个传递给这个工厂?
  • 最外面的实例是我所追求的,与继承的情况相同。

标签: c# design-patterns decorator


【解决方案1】:

将实际的doSomething 设为需要装饰对象作为参数的方法:

节点

public void DoSomething()
{
    this.DoSomethingWith(this)
}
public void DoSomethingWith(INode it)
{
    // ...

    var someInstance = someFactory.Create(it);

    // ...
}

LockableNode

public void DoSomething()
{
    this.innerNode.DoSomethingWith(this);
}
public void DoSomethingWith(INode it)
{
    this.innerNode.DoSomethingWith(it);
}

编辑:当然,您也必须更改界面。

public interface INode
{
    bool IsReadOnly { get; }
    void DoSomething();
    void DoSomethingWith(INode it);
}

【讨论】:

  • 我最终使用其中一种主要方法来完成此操作,只是为了让它首先工作。但是,设计仍然过于脆弱,因为很容易忘记在某个地方实现某些东西。实际上,除了简单的级联之外的任何事情都太复杂了。这是一个拥有数百名用户的开源项目,API 应该直观易用。目前,我已经恢复到继承,因为它似乎涵盖了所有基础,但我仍然对其他想法持开放态度。
  • 我收回这一点,这并不太复杂。我会多考虑的。
  • 如果你想简化装饰器的创建,你可以将它的一部分移动到抽象装饰器中。用法应该还是和以前一样简单
【解决方案2】:

总之,就我而言,答案是使用继承。我确信装饰器模式在某处有其用途,但是向域对象添加功能以进行跨成员调用并将对自身的引用传递给其他对象不是它,特别是如果您无法控制何时或如何将来会在代码中插入其他引用或跨成员调用。

我发现另一个帖子有一些我没有尝试过的想法:

A problem when using the decorator design pattern

【讨论】:

    猜你喜欢
    • 2011-06-23
    • 2016-11-25
    • 2012-03-22
    • 2011-01-02
    • 2019-01-18
    • 2019-06-28
    • 2019-02-03
    相关资源
    最近更新 更多