17

現在、UIからASP.Netアプリを作成しています。私はWinformsにうんざりしていて、関心の分離がより優れたものが欲しかったので、MVPアーキテクチャを実装しています。

したがって、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検証コントロールを使用してメインフォームの検証を実行しましたが、データがサービスレイヤーの基準を十分に満たしていることを確認する必要があります。

次のサービスレイヤーメッセージが表示されるとしましょう。

  • メールアカウントは既に存在します(失敗)
  • 入力した参照ユーザーが存在しません(失敗)
  • パスワードの長さがデータストアの許容長を超えています(失敗)
  • メンバーが正常に作成されました(成功)

また、UIが予測できないより多くのルールがサービスレイヤーに含まれるとしましょう。

現在、計画どおりに進まなかった場合、サービスレイヤーに例外をスローさせています。それは十分な戦略ですか?このコードはあなたたちに臭いがしますか?このようなサービスレイヤーを作成した場合、このように使用するプレゼンターを作成する必要があることに悩まされますか?リターンコードは古すぎるように思われ、ブール値は十分な情報を提供しません。


OPではなく編集:OPによって回答として投稿されたフォローアップコメントにマージ


Cheekysoft、ServiceLayerExceptionの概念が好きです。予期しない例外のためのグローバル例外モジュールがすでにあります。これらすべてのカスタム例外を面倒にすると思いますか?基本の例外クラスをキャッチするのは少し臭いと思っていましたが、そこからどのように進行するのか正確にはわかりませんでした。

tgmdbm、ラムダ式の巧妙な使用が好きです!


フォローアップしてくれたCheekysoftに感謝します。したがって、例外が処理されない場合にユーザーに別のページ(私は主にWeb開発者)が表示されることを気にしないのであれば、それが戦略になると思います。

ただし、ユーザーがエラーの原因となったデータを送信したのと同じビューでエラーメッセージを返したい場合は、プレゼンターで例外をキャッチする必要がありますか?

PresenterがServiceLayerExceptionを処理したときのCreateUserViewは次のようになります。

ユーザーを作成する

この種のエラーの場合は、同じビューに報告すると便利です。

とにかく、私たちは今、私の最初の質問の範囲を超えていると思います。あなたが投稿したものをいじってみます。さらに詳細が必要な場合は、新しい質問を投稿します。

4

3 に答える 3

15

それは私にはちょうどいいですね。例外は、サービス メソッド実装内でどれほど深くネストされていても、サービス層内のどこからでもサービス層の最上部にスローできるため、望ましいものです。これにより、呼び出し元のプレゼンターが常に問題の通知を受け取ることがわかっているため、サービス コードがクリーンに保たれます。

例外をキャッチしない

ただし、プレゼンターで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 ) ブロックを追加して、別のビューでシステム エラーを処理します。

私は常に、自分のサービス レイヤーを個別の配布可能なライブラリと考え、意味のある限り具体的な例外をスローするようにしています。特定の詳細について心配する必要があるか、単に問題を一般的なエラーとして扱う必要があるかを決定するのは、プレゼンター/コントローラー/リモート サービスの実装次第です。

于 2008-08-22T10:33:43.903 に答える
3

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( ... );
} );

次に、エラーが空です。準備完了です。

これをさらに進めて、一般的でない例外を処理するカスタム例外ハンドラーで拡張できます。

于 2008-08-22T14:57:41.133 に答える
1

フォローアップの質問に答えて:

面倒な例外の作成に関しては、ちょっと慣れます。適切なコード ジェネレーターまたはテンプレートを使用すると、最小限の手作業で約 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>
于 2008-08-24T13:25:28.593 に答える