【问题标题】:Reprinting from an application with Command/Decorator Pattern and Simple Injector使用命令/装饰器模式和简单注入器从应用程序重新打印
【发布时间】:2015-03-09 15:33:11
【问题描述】:

所以我在处理某些命令后使用装饰器进行打印。我的问题是用户是否想重印。我创建了一个从 UI 层发送的Reprint 命令类,但是Reprint 命令不需要与PrintDecorator 分开的处理程序,因为重印处理正是打印装饰器中的所有内容。是否有策略仅针对PrintDecorator 使用SimpleInjector?我知道这可能违背了模式,但我能想到的唯一方法是为 reprint 命令创建一个空的命令处理程序,但这似乎不正确。谢谢。

  public class Reprint : ICommandParam, IPrintFrom
  {
    public string Id { get; set; }
    public string Printer { get; set; }
    public int Copies { get; set; }
  }

  public class PrintDecorator<TCommand> : ICommandHandler<TCommand>
    where TCommand : IPrintFrom
  {
    private readonly IFooService _svc;
    private readonly ICommandHandler<TCommand> _handler;

    public PrintDecorator(IFooService svc, ICommandHandler<TCommand> handler)
    {
      if (svc == null)
        throw new ArgumentNullException("IFooService");

      _svc = svc;
      _handler = handler;
    }

    [Import] // optional
    public IDatabase Database { get; set; }

    public void Handle(TCommand commandParm)
    {
      if (_handler != null)
        _handler.Handle(commandParm);

      svc.GetDataFromService(commandParm.id);
      svc.PrintData(commandParm.Printer, commandParm.Copies);    
      if (Database != null && commandParm.Copies > 0) {
        // TODO - add a print record
      }
    }
  }

【问题讨论】:

  • 我发现你的空检查有点吓人。为什么你的装饰者没有数据库或没有被装饰者是有效的?通常,您应该通过在构造函数中注入所有必需的依赖项并在其中添加保护语句来防止无效状态。这可以防止您的代码因空检查而变得复杂。
  • 关于那些“可选依赖项”,请查看this article of mine,它解释了为什么“依赖项几乎不应该是可选的”。
  • 对于空检查,想法是只有在跟踪打印时才需要数据库,在我看来这是可选的。我最初将处理程序检查为 null,因为我认为我可以直接将 Reprint 命令与装饰器一起使用,而无需处理程序,我现在可以删除它。

标签: c# decorator simple-injector


【解决方案1】:

这一切都取决于你想要什么。我的建议是将重印逻辑保留在真正的ReprintCommandHandler 中(可能是通过注入一个服务,该服务以与装饰器相同的方式进行打印)。这在我看来是合理的,因为在转载的情况下,转载是实际的业务逻辑,而不是横切关注点。

在这种情况下,您必须将 PrintDecorator 排除在您的 ReprintCommandHandler 周围进行装饰,这可以按如下方式完成:

container.RegisterDecorator(
    typeof(ICommandHandler<>),
    typeof(PrintDecorator<>),
    c => c.ServiceType != typeof(ICommandHandler<Reprint>));

另一方面,如果您想将打印逻辑保留在PrintDecorator 中,而不必在业务层中复制此逻辑,您可以实现一个完全空的ReprintCommandHandler,或者您可以注册一个特殊的@ 987654321@ 命令处理程序。

使用空处理程序当然很简单,并且会让您的配置非常简单:

// NOTE: Use RegisterManyForOpenGeneric for Simple Injector v2.x
container.Register(typeof(ICommandHandler<>),
   new[] { typeof(ICommandHandler<>).Assembly });

container.RegisterDecorator(
    typeof(ICommandHandler<>),
    typeof(PrintDecorator<>));

当然,缺点是您需要一个空类。所以另一种方法是按如下方式实现 Null Object 模式:

public class NullCommandHandler<T> : ICommandHandler<T> {
    public void Handle(T command) { }
}

如果您有多个空实现,则可以重用此实现,您可以按如下方式注册它:

// NOTE: Use RegisterManyForOpenGeneric for Simple Injector v2.x
container.Register(typeof(ICommandHandler<>),
   new[] { typeof(ICommandHandler<>).Assembly });

container.Register<ICommandHandler<Reprint>, NullCommandHandler<Reprint>>();

container.RegisterDecorator(
    typeof(ICommandHandler<>),
    typeof(PrintDecorator<>));

【讨论】:

  • 如果装饰器之外的转载没有额外的逻辑,看来我必须创建一个空的ReprintCommandHandlerSimpleInjector会告诉我ICommandHandler&lt;Reprint&gt;没有注册,因为对现在我没有 Reprint 类的处理程序,并且将仅依赖装饰器。我没听错吗?
  • @JayMichael:对不起。我的回答不是很清楚。请查看我的更新。
  • 嗯,这给了我一些思考。我过去成功地使用了空对象模式,但也许将实际的打印逻辑放在处理程序使用的服务中可能对未来更好。感谢@Steven 的帮助
  • @JayMichael:这对我来说似乎很可靠。我的装饰器通常只会将调用委托给注入的服务。不一定要这样,但我经常想重用相同的逻辑(跨装饰器和系统的各个部分),在这种情况下,它应该被提取到一个单独的服务中。
  • 谢谢。取出IFooService 并带入我的IDispatcher 并从装饰器调用重印命令处理程序会令人不悦吗? (例如_dispatcher.Dispatch(new Reprint())。 IDispatcher 在 Web 层中用于向其处理程序发送命令和查询。
猜你喜欢
  • 2012-06-17
  • 2012-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-07
  • 2011-02-02
相关资源
最近更新 更多