Когда операция должна передать больше, чем просто результат, вы кортежуете / выбрасываете / или 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
,