Когда операция должна передать больше, чем просто результат, вы кортежуете / выбрасываете / или getContextual?

Я пытаюсь реорганизовать некоторый код "отправки по электронной почте", разделив шаги (проверка, присоединение связанного содержимого, форматирование, отправка) на отдельные классы, которые легче тестировать, регистрировать и обновлять.

В рамках этого я должен выяснить, каким образом операции должны сообщать отправителю о проверке или временных ошибках ("этот элемент был удален"), чтобы он мог запросить у пользователя дополнительную информацию или сообщить ему плохие новости., Вот путь, по которому идет нить (да, каракули)

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

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

Итак, вы вдумчивый тип, между "возвращением", "исключениями" или "контекстом"... какой из них делает вас счастливее?

A. Бросьте исключение при любой проблеме и позвольте контроллеру разделить те, с которыми он может работать изящно, и те, которые знают мой "звуковой сигнал".

Б. Вернуть какой-то результат<T>класс для переноса как продукта операций (по электронной почте), так и перечисленных результатов различных операций.

C. Передайте контекст в / из всех шагов, где они могут указать любые параметры, с которыми они не могут иметь дело, и сохраните сигнатуры метода очень простыми.

Д. Сынок, ты думаешь об этом.. Вот что ты собираешься делать: <YourSpecialJujuHere/>

Спасибо за любой вклад, вы выступаете на концерте.

2 ответа

Решение

Вы можете использовать шаблон 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);
        }
    }
}

Я предпочитаю использовать исключения, когда код должен отклоняться от Счастливого Пути. Я чувствую, что они держат логику и обработку ошибок четко разделенными.

Редактировать: возврат из метода SendMessage указывает вызывающей стороне, прошла ли валидация или нет, и что не прошло валидацию. Затем вызывающий абонент может запросить у пользователя дополнительную информацию и повторить попытку или указать успешное выполнение. Исключение выдается только в случае действительно исключительного обстоятельства.

Используя этот подход, каждый компонент вашего алгоритма может быть смоделирован и протестирован независимо, и ни одна из Стратегий не должна знать, как работает любая другая Стратегия, и не должна знать, как обрабатывать чужие ошибки. Наконец, вся ваша обработка ошибок централизована в одном месте.

Возможно, проблема заключается в последовательности действий, которая заставляет действие вызывать следующее.

Другой подход - заставить Контроллер вызывать все действия по очереди. В этом случае существует прямая связь между вашим контроллером и каждым действием.

Каждое действие может возвращать простой результат или сигнализировать об ошибке способом, соответствующим его случаю:

  • Исключительные ситуации через исключение
  • Нет результата через нулевой возврат.
  • ...

Повторное использование от одного действия к другому может происходить как локальные переменные.

Пример кода (добавьте параметры и т. Д. При необходимости):

    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,

Другие вопросы по тегам