处理可能具有不同类型结果的函数的模式

时间:2010-05-31 21:58:58

标签: design-patterns oop exception

假设你有一个对象的方法给定一些输入改变对象状态如果输入根据一些复杂的逻辑验证。

现在假设当输入未验证时,可能是由于几个不同的事情,我们希望以不同的方式处理每个事物。

我相信很多人都在想:这就是例外情况!我也想过这个。

但我对使用异常的保留意见是,在某些情况下,输入未验证没有任何例外,我真的希望避免使用异常来控制程序预期流程中的实际内容。

如果只有一种解释可能,我可以简单地选择返回一个布尔值,指示操作是否导致状态改变,如果没有,则返回适当的响应。

当然还可以选择返回客户端可以选择解释的状态代码。我不喜欢这么多,因为状态代码没有任何语义。

我到目前为止的解决方案是始终检查我能够处理的每个可能情况之前我调用该方法然后返回一个布尔值来通知客户端对象是否更改状态。这使我能够根据我所处的上下文灵活处理尽可能少或尽可能多的情况。它还有一个好处,就是让我调用的方法更简单。缺点是无论我在哪里调用该方法,客户端代码都会有很多重复。

您更喜欢哪种解决方案?为什么?

人们使用哪些其他模式从功能中提供有意义的反馈?我知道有些语言支持多个返回值,如果我有这个选项,我肯定会喜欢它。

5 个答案:

答案 0 :(得分:1)

返回值不是方法的唯一输出值,您可以使用“out”参数,或将其他对象作为引用传递。例如,除了要验证的输入之外,您还可以将另一个对象传递给该方法 - 一个“跟踪器”,该方法可以填写输入有效性的详细信息,以及状态更改的详细信息。 p>

您仍然可以将布尔值保留为一般成功/失败标志。某些客户端可能不关心细节,并且将仅为返回值传递跟踪器的空引用。其他客户可能需要所有详细信息,因此仅使用跟踪器来确定状态是否已更改。

最重要的是,通过允许客户端传入跟踪器,客户端可以指示要收集的详细信息并决定对信息执行的操作。其中一些可能是昂贵的计算,或沉重的内存来保持。将跟踪器作为参数而不仅仅是返回值,允许客户端提供最合适的实现。跟踪器类可以包括客户端设置的标志和其他信号,这些信号向验证器指示客户端从验证中想要的信息。

为方便起见,您还可以使用返回跟踪器的方法,但这确实是语法糖。例如。 (在psudo-java中)

   interface ValidationResult  {
      List<ValidationError> validationErrors();  
      boolean isValid();      
      List<StateChange> stateChanges();
      boolean hasChanged();
   }

   public class AbstractValidationResult extends ValidationResult {
      // provides setters for properties. Make setter available to
      // the StateChangeValidator, but hides these from the ValidationResult
      // interface that general clients use.
      public void setValid(boolean valid) {
          this.valid = valid;
      }
   }

   class DefaultValidationResult : AbstractValidationResult {
       // default implementation
   }

   // your input validationg/state changing class
   class StateChangeValidator 
   {
        // convenience method
        public ValidationResult setState(Input intput)
        {
           return setState(input, new DefaultValidationResult());
        }

        // implementation allows clients to specify how the validation is handled.
        public ValidationResult setState(Input input, AbstactValidationResult result)
        {
           validate(input, result);
           changeState(input);
           return result;
        }

        // just validation - no actual state change
        public ValidationResult validate(Input input, AbstractValidationResult result)
        {
           result.setvalid(!hasErrors);
           return result;               
        }
   }

您选择哪种方法,您应该能够避免在课堂外重复验证行为。

在对象上分离出验证输入但实际上不进行状态更改的“验证”方法也很有用。在预先验证输入时,例如在UI中启用“确定”按钮时,此模式非常有用。

答案 1 :(得分:1)

我认为这是一个使用基元的情况,当你最好使用对象时。我通过返回状态对象在我自己的代码中处理这个问题。这些对象允许您封装一个简单的“工作/不工作”布尔标志,并提供有关失败原因的其他信息,或其他相关元数据,例如某些状态更改的描述。

这类似于mdma's answer中描述的“跟踪器”构思。状态对象的内在深度取决于客户端。不关心细节的客户只需检查status.was_successful()方法即可。不同之处在于我会直接返回“跟踪器”,而不是将其作为参考传递。这使调用接口更加简单。

返回一个对象也可以有效地处理对多个返回参数的任何需求,因为您可以在单个状态对象中封装所需的所有内容。

答案 2 :(得分:0)

如果不使用普通数字并将其替换为某种常量,则状态代码可以是语义的。例如,在PHP中处理文件上载时,您需要处理显式常量,如UPLOAD_ERR_INI_SIZEUPLOAD_ERR_FORM_SIZE等。当然,当你使用像1,2,3这样的普通数字时它会变得混乱,因为你不知道它们的意思。但常量为状态代码提供了一种优雅的方式来表达某些东西。

答案 3 :(得分:0)

答案 4 :(得分:0)

认为可以使用的是lambda / closure(取决于你需要的东西)。 在这里,你要做的就是小事。

dic at: #foo ifAbsent: [ 'do something interesting' ]

在这里你可以看到,只要你在字典中找不到某些内容,就可以评估该块。 这可能适用于您的问题:每当您不验证输入时,请通过评估闭包来询问发件人该做什么。

简单,不是吗?