Как вы сообщаете сообщения / ошибки сервисного уровня верхним уровням, используя MVP?

В настоящее время я пишу приложение ASP.Net из пользовательского интерфейса вниз. Я реализую архитектуру MVP, потому что я устал от Winforms и хотел чего-то, что было лучше разделить проблемы.

Таким образом, с MVP Presenter обрабатывает события, вызванные View. Вот некоторый код, который я использовал для создания пользователей:

public class CreateMemberPresenter
{
    private ICreateMemberView view;
    private IMemberTasks tasks;

    public CreateMemberPresenter(ICreateMemberView view) 
        : this(view, new StubMemberTasks())
    {
    }

    public CreateMemberPresenter(ICreateMemberView view, IMemberTasks tasks)
    {
        this.view = view;
        this.tasks = tasks;

        HookupEventHandlersTo(view);
    }

    private void HookupEventHandlersTo(ICreateMemberView view)
    {
        view.CreateMember += delegate { CreateMember(); };
    }

    private void CreateMember()
    {
        if (!view.IsValid)
            return;

        try
        {
            int newUserId;
            tasks.CreateMember(view.NewMember, out newUserId);
            view.NewUserCode = newUserId;
            view.Notify(new NotificationDTO() { Type = NotificationType.Success });
        }
        catch(Exception e)
        {
            this.LogA().Message(string.Format("Error Creating User: {0}", e.Message));
            view.Notify(new NotificationDTO() { Type = NotificationType.Failure, Message = "There was an error creating a new member" });
        }
    }
}

Моя основная проверка формы выполнена с использованием встроенных .Net Validation Controls, но теперь мне нужно убедиться, что данные в достаточной степени удовлетворяют критериям для уровня обслуживания.

Допустим, могут отображаться следующие сообщения уровня сервиса:

  • Учетная запись электронной почты уже существует (ошибка)
  • Введенный пользователь не существует (ошибка)
  • Длина пароля превышает допустимую длину хранилища данных (ошибка)
  • Участник создан успешно (успех)

Скажем также, что на уровне сервиса будет больше правил, которые пользовательский интерфейс не может предвидеть.

В настоящее время у меня есть сервисный слой, создающий исключение, если все идет не так, как планировалось. Это достаточная стратегия? Парни, этот код пахнет? Если бы я написал такой сервисный слой, вы были бы раздражены необходимостью писать докладчики, которые используют его таким образом? Код возврата кажется слишком старой школой, а бул просто недостаточно информативен.


Редактировать не по OP: объединение последующих комментариев, которые были опубликованы как ответы OP


Cheekysoft, мне нравится концепция исключения ServiceLayer. У меня уже есть глобальный модуль исключений для исключений, которые я не ожидаю. Считаете ли вы делать все эти пользовательские исключения утомительными? Я думал, что ловить базовый класс Exception было немного вонючим, но я точно не знал, как продвигаться дальше.

tgmdbm, мне нравится умное использование там лямбда-выражения!


Спасибо Cheekysoft за продолжение. Поэтому я предполагаю, что это будет стратегией, если вы не против того, чтобы пользователю отображалась отдельная страница (я в первую очередь веб-разработчик), если исключение не обрабатывается.

Однако, если я хочу вернуть сообщение об ошибке в том же представлении, где пользователь отправил данные, вызвавшие ошибку, мне бы пришлось перехватывать исключение в Presenter?

Вот как выглядит CreateUserView, когда Presenter обработал исключение ServiceLayerException:

Создать пользователя

Для такого рода ошибок, хорошо сообщить о том же представлении.

В любом случае, я думаю, что мы сейчас выходим за рамки моего первоначального вопроса. Я поиграю с тем, что вы написали, и если мне понадобится дополнительная информация, я опубликую новый вопрос.

22.08.2008 03:12:59
3 ОТВЕТА
РЕШЕНИЕ

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

Не лови исключение

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

Планирование простой иерархии исключений

Если вы собираетесь использовать исключения таким образом, вы должны разработать иерархию исключений для ваших собственных классов исключений. Как минимум создайте класс ServiceLayerException и добавьте один из них в ваши методы обслуживания при возникновении проблемы. Затем, если вам нужно сгенерировать исключение, которое / должно быть по-разному обработано докладчиком, вы можете сгенерировать определенный подкласс ServiceLayerException: скажем, AccountAlreadyExistsException.

Ваш докладчик затем имеет возможность сделать

try {
  // call service etc.
  // handle success to view
} 
catch (AccountAlreadyExistsException) {
  // set the message and some other unique data in the view
}
catch (ServiceLayerException) {
  // set the message in the view
}
// system exceptions, and unrecoverable exceptions are allowed to bubble 
// up the call stack so a general error can be shown to the user, rather 
// than showing the form again.

Использование наследования в ваших собственных классах исключений означает, что вы не обязаны перехватывать множественные исключения в своем презентаторе - вы можете, если в этом есть необходимость, - и вы не в конечном итоге случайно перехватываете исключения, которые вы не можете обработать. Если ваш докладчик уже находится на вершине стека вызовов, добавьте блок catch (Exception) для обработки системных ошибок в другом представлении.

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

15
23.05.2017 10:27:42
Какой хороший ответ :)
Mik378 14.06.2013 10:20:27

Как предполагает Cheekysoft, я бы хотел перенести все основные исключения в ExceptionHandler и позволить этим исключениям всплыть. ExceptionHandler будет отображать соответствующее представление для типа исключения.

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

public static class Try {
    public static List<string> This( Action action ) {
      var errors = new List<string>();
      try {
        action();
      }
      catch ( SpecificException e ) {
        errors.Add( "Something went 'orribly wrong" );
      }
      catch ( ... )
      // ...
     return errors;
    }
}

Тогда при звонке в вашу службу просто сделайте следующее

var errors = Try.This( () => {
  // call your service here
  tasks.CreateMember( ... );
} );

Тогда по ошибкам пусто, тебе хорошо идти.

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

3
22.08.2008 14:57:41

В ответ на дополнительный вопрос:

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

Тем не менее, во многих реальных приложениях обработка ошибок может составлять 70% работы, так что на самом деле это всего лишь часть игры.

Как подсказывает tgmdbm, в приложениях MVC / MVP я позволил всем своим необработанным исключениям всплыть наверх и попасть в диспетчер, который делегирует ExceptionHandler. Я настроил его так, чтобы он использовал ExceptionResolver, который просматривает файл конфигурации, чтобы выбрать подходящее представление для отображения пользователю. Java Spring MVC библиотека делает это очень хорошо. Вот фрагмент из файла конфигурации для распознавателя исключений Spring MVC - для Java / Spring, но вы поймете идею.

Это отнимает огромное количество обработчиков исключений у ваших докладчиков / контролеров.

<bean id="exceptionResolver"
      class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

  <property name="exceptionMappings">
    <props>
      <prop key="UserNotFoundException">
        rescues/UserNotFound
      </prop>
      <prop key="HibernateJdbcException">
        rescues/databaseProblem
      </prop>
      <prop key="java.net.ConnectException">
        rescues/networkTimeout
      </prop>
      <prop key="ValidationException">
        rescues/validationError
      </prop>
      <prop key="EnvironmentNotConfiguredException">
        rescues/environmentNotConfigured
      </prop>
      <prop key="MessageRejectedPleaseRetryException">
        rescues/messageRejected
      </prop>
    </props>
  </property>
  <property name="defaultErrorView" value="rescues/general" />
</bean>
1
24.08.2008 13:25:28