1

今日は「教えて!聞かないで!」について考えていました。このコードを試しています。

インターフェース:

interface IValidationContext
{
  void AddMessage(string text);   
  bool IsValid { set; }
}

interface IValidation
{
  void ValidateInput(Input input, IValidationContext context); 
  void ValidateOutput(Output output, IValidationContext context); 
}

interface ICalculator
{
  Output Calculate(Input input);
}

実装:

class CalculationService
{
  private readonly ICalulator _calculator;
  private readonly IValidation _validation;

  public CalculationService(ICalculator calculator, IValidation validation)
  {
    _calculator = calculator;
    _validation = validation;
  }

  public Output Calculate(Input input)
  {
     var context = new CalculationContext();
     _validation.ValidateInput(input, context);

     if (context.IsValid)
     {
        var output = _calculator.Calculate(input);
        _validation.ValidateOutput(output, context);
        return output
     }
     return null;
  }
}

class CalculationContext : IValidationContext
{
  public CalculationContext()
  {
    Messages = new List<string>();
  }

  public IList<string> Messages { get; private set; }

  public void AddMessage(string text)
  {
    Messages.Add(text);
  }

  public bool IsValid { set; get; }
}

設計原則に準拠することが常に可能であるとは限らないことを私は知っています。しかし、最終的に私はオブジェクトを求めているこのコードに固執しました:

 if (context.IsValid)
 {
   var output = _calculator.Calculate(input);
   _validation.ValidateOutput(output, context);
 }

実用的かどうかにかかわらず、それを解決することは可能ですか?

編集 1:
変更してIValidationContext名前を変更した場合:

interface ICalculationContext
{
   void AddMessage(string text);   
   Output Calculate(ICalculator calculator, Input input);
   bool IsValid { set; }
}

コンテキストを尋ねる必要はありません:

public Output Calculate(Input input)
{
  _validation.ValidateInput(input, context);        
  var output = context.Calculate(_calculator, input);
  _validation.ValidateOutput(output, context);
  return output;
}

現在、コンテキストは内部状態に基づいて計算を呼び出す責任があります。...気分が悪い...

編集2:
「教えて!聞かないで!」についての小さな記事を読みました。オブジェクトにその内部状態を尋ねてから、その状態に応じてオブジェクトに何かを伝えることは、「教えてください! 尋ねないでください!」に違反します。しかし、別のオブジェクトに何かを伝えることは問題ありません。これはここに当てはまりますか?

ところで。と の boolean-isvalid-result を導入しValidateInputますValidateOutput。コードを次のように変更できます。

public Output Calculate(Input input)
{
  var isValid = _validation.ValidateInput(input, context);

  if (isValid)
  {
    var output = _calculator.Calculate(input);
    _validation.ValidateOutput(output, context);
    return output
  }
  return null;
}
4

1 に答える 1

3

この行は問題の原因です

var context = new CalculationContext();

クラス外で問い合わせできるように、CalculationContextをに注入する必要があります。CalculationServiceこのCalculateメソッド、入力と出力を検証するようにバリデーターに指示します。 このコンテキストで尋ねると、次のようなコードになります。

public Output Calculate(Input input)
{
   var validator = _context.Validator;
   if (validator.IsInputValid(input)) {
       // ... snip ...
   }
}

ここでは、何かを検証するように指示するのではなく、特定の入力が有効かどうかをバリデーターに尋ねています。IsValidまた、オブジェクトのゲッターの操作に限定していIValidationContextます。アクセスはデメテルの法則に違反していると見なされる可能性があるため、これは少しあいまいな状況です_context.Validatorが、このプロパティはインターフェイスで定義され、インターフェイスを返すだけなので、これらのクラスの具体的な実装には結合されません。

インターフェイスへの次の変更を想定して、ここに提案があります

interface IValidationContext
{
    void AddMessage(string text); 
    IValidation Validator { get; }
    bool IsValid { get; }
}

interface IValidation
{
    void ValidateInput(Input input); 
    void ValidateOutput(Output output); 
}

interface ICalculator
{
    Output Calculate(Input input);
}


class CalculationService
{
    private readonly ICalulator _calculator;
    private readonly IValidationContext _context;

    public CalculationService(ICalculator calculator, IValidationContext context)
    {
      _calculator = calculator;
      _context = context;
    }

    public Output Calculate(Input input)
    {
       _context.Validator.ValidateInput(input);

       if (_context.IsValid)
       {
          var output = _calculator.Calculate(input);
          _context.Validator.ValidateOutput(output);

          return output;
       }
       return null;
    }
}
于 2012-08-21T18:41:40.143 に答える