【问题标题】:Castle, sharing a transient component between a decorator and a decorated componentCastle,在装饰器和装饰组件之间共享一个瞬态组件
【发布时间】:2010-05-28 08:11:01
【问题描述】:

考虑以下示例:

public interface ITask
{
    void Execute();
}

public class LoggingTaskRunner : ITask
{
    private readonly ITask _taskToDecorate;
    private readonly MessageBuffer _messageBuffer;

    public LoggingTaskRunner(ITask taskToDecorate, MessageBuffer messageBuffer)
    {
        _taskToDecorate = taskToDecorate;
        _messageBuffer = messageBuffer;
    }

    public void Execute()
    {
        _taskToDecorate.Execute();
        Log(_messageBuffer);
    }

    private void Log(MessageBuffer messageBuffer)
    {}
}

public class TaskRunner : ITask
{
    public TaskRunner(MessageBuffer messageBuffer)
    {

    }

    public void Execute()
    {
    }
}

public class MessageBuffer
{

}


public class Configuration
{
    public void Configure()
    {
        IWindsorContainer container = null;

        container.Register(
            Component.For<MessageBuffer>()
                .LifeStyle.Transient);

        container.Register(
            Component.For<ITask>()
                .ImplementedBy<LoggingTaskRunner>()
                .ServiceOverrides(ServiceOverride.ForKey("taskToDecorate").Eq("task.to.decorate")));

        container.Register(
            Component.For<ITask>()
            .ImplementedBy<TaskRunner>()
            .Named("task.to.decorate"));

    }

}

如何让 Windsor 实例化“共享”瞬态组件,以便“装饰器”和“装饰器”获得相同的实例?

编辑:由于设计受到批评,我发布的内容更接近应用程序中正在执行的操作。也许有人可以提出更好的解决方案(如果在记录器和真实任务之间共享瞬态资源被认为是一个糟糕的设计)

Edit2:Castle3 通过引入“绑定”生活方式增加了对此 (http://docs.castleproject.org/Windsor.Whats-New-In-Windsor-3.ashx) 的支持

【问题讨论】:

  • 根据您的编辑,您根本不需要 TaskRunner 中的 MessageBuffer,因为它没有被使用。我怀疑我们仍在寻找与手头实际问题相去甚远的东西?我知道很难提炼出一个问题,以便可以轻松地传达它而不会使其无法识别,但是根据手头的信息,您可以通过从 TaskRunner 中删除 MessageBuffer 依赖项来解决问题...
  • 为简洁起见,我只是删除了该用法 :-) 装饰任务会将消息放入消息缓冲区,而装饰的日志记录会将消息从缓冲区放入记录器(log4net、控制台输出等)

标签: castle-windsor ioc-container


【解决方案1】:

“瞬态”明确表示“非共享”,因此您所要求的在概念上是错误的。 正确的解决方案是将Shared注册为Singleton而不是Transient:

container.Register(Component.For<Shared>());

(单例是 Windsor 中的默认生命周期。)

但是,我怀疑上述问题的背后隐藏着一个更复杂的问题。我猜你需要 Shared 是 Transient,因为在很多其他情况下你需要这种生活方式,但是当涉及到 Decorator 和 Decorated 之间的关系时,你需要分享它们。

我仍然认为这听起来像是一种设计气味,但至少有两种方法可以实现此结果。

第一个选项涉及过早解析Shared,并将解析的实例显式提供给两个 IFoo 注册的配置:

container.Register(Component.For<Shared>().LifeStyle.Transient);

var r = container.Resolve<Shared>();

container.Register(Component
    .For<IFoo>()
    .ImplementedBy<Decorator>()
    .DependsOn(new { resource = r }));
container.Register(Component
    .For<IFoo>()
    .ImplementedBy<Decorated>()
    .DependsOn(new { resource = r }));

第二个选项是为Shared 进行专门的命名注册,仅由 IFoo 注册使用:

container.Register(Component.For<Shared>().LifeStyle.Transient);
container.Register(Component.For<Shared>().Named("shared"));
container.Register(Component
    .For<IFoo>()
    .ImplementedBy<Decorator>()
    .ServiceOverrides(new { resource = "shared" }));
container.Register(Component
    .For<IFoo>()
    .ImplementedBy<Decorated>()
    .ServiceOverrides(new { resource = "shared" }));

【讨论】:

  • 我不想让“共享”成为非瞬态的,因为它有状态。我也不想将其作为构造函数依赖项删除,因为它是一个必须满足的不变量,将其变为属性而不是 ctor 依赖项将改进类设计以适应 IoC 容器的工作原理。跨度>
  • 对状态的关注是你不想共享实例的一个非常正当的理由,但是Shared 在装饰器和装饰器之间共享的,所以你已经违反了这个原则。您当前的实现可能表现得“很好”而不是损坏状态,但其他合作者可能不是那么好。换句话说,如果注入不同的依赖项,实现可能会中断。这表明违反了 Liskov 替换原则。
  • 您误解了用法。它适用于任何引用消息缓冲区的类来利用其合同,我的意思是添加消息。我没有假设任何关于消息缓冲区的使用,因此我没有破坏 Liskov。
  • @MarkSeemann 我认为我的问题与 Marius 的问题有相似的用法,请您看一下,stackoverflow.com/questions/25064516/…
猜你喜欢
  • 2020-04-07
  • 2012-02-02
  • 2011-09-03
  • 2016-08-18
  • 2018-04-06
  • 1970-01-01
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
相关资源
最近更新 更多