Как решить, что последнее "спросить" в сложном расчете соответствует "говорите! Не спрашивайте!"?
Сегодня я думал о "скажи! не спрашивай!" и экспериментируя с этим кодом.
интерфейсы:
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:
я прочитал небольшую статью о "скажи! не спрашивай!" и в нем говорится, что: запрос объекта на его внутреннее состояние, а затем указание этому объекту чего-либо, зависящего от этого состояния, приведет к нарушению "говори! не спрашивай!" но можно сказать что-то другому объекту. это применимо здесь?
Кстати. введя логический-isvalid-результат для 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;
}
1 ответ
Эта строка является источником проблемы
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;
}
}