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

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

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

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

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

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


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


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

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


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

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

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

Create a user

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

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

Ответы [ 3 ]

15 голосов
/ 22 августа 2008

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

Не поймать исключение

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

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

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

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

3 голосов
/ 22 августа 2008

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

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

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

1 голос
/ 24 августа 2008

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

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