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

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

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

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

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

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

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

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

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

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

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

Ответы [ 2 ]

4 голосов
/ 03 ноября 2009

Вы можете использовать шаблон 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 указывает вызывающей стороне, прошла ли проверка или нет, и что не удалось проверить. Затем вызывающий абонент может запросить у пользователя дополнительную информацию и повторить попытку или указать на успех. Исключение выдается только в случае действительно исключительного обстоятельства.

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

1 голос
/ 02 ноября 2009

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

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

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

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

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

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

    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.

...