当一个操作需要传递的不仅仅是结果时,你是tuple / throw /还是getContextual?

时间:2009-11-02 17:03:05

标签: design-patterns architecture tdd exception single-responsibility-principle

我正在尝试通过将步骤(验证,附加相关内容,格式,发送)分成可以更容易测试,记录和更新的单独类来重构某些“发送电子邮件”代码。

作为其中的一部分,我必须找出一种方法让操作将验证或瞬态错误(“该项目被删除”)传回给发起人,以便它可以向用户询问更多信息或告诉他们坏消息。这是线程所采用的路径(yay,一个涂鸦)

     "Controller"                     
      .   -> Outbox 
      .         -> Validator 
      .         -> Formatter 
      .         -> Sender
      .   <- 

                   -> Parameters, work in progress
                   <- Good, not so good, "you better sit down" news

所以你是一个深思熟虑的,在“回归”,“例外”或“背景”之间...哪一个让你最开心?

  

一个。在任何问题上抛出异常,让控制器将它可以优雅处理的那些和知道我“蜂鸣器”的那些分开。

     

B中。 返回某种结果<T>类,用于携带操作的产品(电子邮件)和各种操作的枚举结果。

     

℃。将上下文传入/移出所有可以指示无法处理的参数的步骤,并使方法签名非常简单。

     

d。儿子,你是在思考这个问题。以下是你要做的事情:< YourSpecialJujuHere />

感谢所有的贡献,你摇滚乐。

2 个答案:

答案 0 :(得分:4)

您可以将Template Method模式与Strategy模式一起使用:

您的控制器成为模板方法。对于电子邮件发送过程的每个步骤,您将调出一个实现该步骤的委托/策略类。

public class EmailSender
{
    private iOutboxGetter outboxGetter;
    private iMsgValidator validator;
    private iMsgFormatter formatter;
    private iMsgSender    sender;

    //setters for each stragegy, or a constructor
    //good use for IOC container

    public iSendResult SendMessage(iMsgParams params)
    {
        try
        {
            var outbox = outboxGetter.getOutbox(params.outbox);
            var validationResults = validator.validate(params);
            if(validationResults.IsValid)
            {
                var msg = formatter.formatMsg(params.message);
                sender.send(msg);
                return new AllGoodSendResult();
            }
            else
            {
                return new ValidationFailedSendResult(validationResults);
            }
        } 
        catch(CatastrophicException e)
        {
           Pager.SendCriticalPage(e.message);
            return new CatistrophicFailureSendResult(e);
        }
    }
}

当代码必须偏离Happy Path时,我更喜欢使用异常。我觉得他们保持逻辑和错误处理完全分开。

编辑:SendMessage方法的返回向调用者指示验证是否通过,以及验证失败。然后,呼叫者可以提示用户提供更多信息并重试,或指示成功。只有在真正异常的情况下才会抛出异常。

使用这种方法,您的算法的每个组件都可以独立进行模拟和测试,没有策略需要知道其他策略是如何工作的,也不需要知道如何处理其他人的错误。最后,您的所有错误处理都在一个地方进行了中心化。

答案 1 :(得分:1)

也许问题在于动作的排序,这使得动作调用成为下一个动作。

另一种方法是让Controller依次调用所有操作。在这种情况下,您的Controller与每个操作之间存在直接关系。

每个动作都可能返回一个简单的结果,或通过适合其情况的方式发出错误信号:

  • 通过例外的特殊情况
  • 没有通过null返回的结果。
  • ...

从一个动作重用到另一个动作可以作为局部变量发生。

示例代码(根据需要添加参数等):

    class Controller1 {

       private Sender sender = new SenderImpl();

       public void process(String text) {
         try {
           Outbox box = getOutbox();
           List<Errors> errors = validate(text);
           if (!errors.isEmpty()) {
             ....
             return;
           }
           String formatted = format(text);
           sender.send(formatted);
         } catch(MyException e) {
           ....
         }
       }
    }

虽然在此代码中,步骤被委托给同一个类的方法,但是很容易跟随其他类的实例使用相同的结构(但仅在需要时,不要过度工程)。正如您所提到的,这对于可测试性来说是合理的。我更改了sender的示例代码。