Как вы передаете сообщения/ошибки уровня обслуживания на более высокие уровни с помощью MVP?

StackOverflow https://stackoverflow.com/questions/21697

Вопрос

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

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

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, но теперь мне нужно убедиться, что данные в достаточной степени удовлетворяют критериям уровня обслуживания.

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

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

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

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


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


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

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


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

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

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

Create a user

Об ошибках такого рода полезно сообщить об этом в том же представлении.

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

Это было полезно?

Решение

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

Не перехватывать исключение

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

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

Если вы собираетесь использовать исключения таким образом, вам следует разработать иерархию исключений для ваших собственных классов исключений.Как минимум создайте класс 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) для обработки системных ошибок в другом представлении.

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

Другие советы

Как предлагает 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( ... );
} );

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

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

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

Что касается создания исключений, то к этому можно привыкнуть.Использование хорошего генератора кода или шаблона позволяет создать класс исключения с минимальным ручным редактированием примерно за 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>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top