Подтвердить что ты не робот

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

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

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

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

Скажем, следующие сообщения Service Layer могут отображаться:

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

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

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


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


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

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


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

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

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

Create a user

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

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

4b9b3361

Ответ 1

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

Не использовать исключение

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

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

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

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

Ответ 2

Как говорит 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( ... );
} );

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

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

Ответ 3

В ответ на следующий вопрос:

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

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

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