41

コマンドパターンに関連して、設計について話し合ったことがあります。私の仲間は、.execute() メソッドが呼び出された後、コマンド オブジェクトがステータス (成功、失敗、およびその理由) を返すべきではないと述べました。その理由は、コマンドには状態が含まれていてはならないため、コマンドが実行されるかどうかを気にする必要がないからです。ただし、コマンドが期待どおりの効果をもたらしたかどうかは、呼び出し後に確認する必要があります。彼が主張したもう 1 つのポイントは、ギャング オブ フォーでは、コマンド パターンがこのケース (状態を返す) を示さないということでした。

私は反対の点を主張しました。GoF はこのケースを提示しませんが、必要に応じてパターンをモデル化できます。コマンドが失敗した場合、呼び出し元のクライアントはステータスの証明を受け取り、最終的に適切な反応を展開する必要があります。アクションが成功したかどうかをクライアントに確認させることにより、エラーが発生しやすく、重複したコードが生成されました。さらに、コマンドが結果を生成する場合があります (たとえば、プロットに行を追加するコマンドは、何らかの形でクライアントに返す行 ID を持っています)、状態のないコマンドを持っているふりをすることは、データモデルから新しいオブジェクト識別子を「釣り上げる」。

最終的に、ステータスを返さず、新しく作成されたオブジェクトの ID をコマンド オブジェクトに保持するという妥協点に達し、アプリケーションはとにかくうまく機能しましたが、あなたの意見も知りたいと思っています。

4

9 に答える 9

28

現時点では、デザイン パターン: Elements of Reusable Object-Oriented Software を目の前に持っていませんが、著者が提示するデザイン パターンは、特定の目的に合わせて変更できるモデルであるとさえ言っていると確信しています。状況。

この質問は、デザイン パターンとは何か、つまりテンプレートの核心に迫るものです。それは本によって実装されなければならないものではありません。本に示されているように、パターンを論理的に変更することでアプリケーションが改善されるケースを特定しました。これは、特にメリットとコストを比較検討した場合には、まったく問題ありません。

于 2009-07-20T18:00:52.467 に答える
13

質問には、複数の回答がある 2 つの質問があります:) 最初の質問は、コマンドがエラー状態を返す必要があるかどうかです。

どのプログラムにも明確な答えはありません。パターンを適用するたびに、もう一度考え直さなければなりません。

考慮する必要があることの 1 つは、次のとおりです。

  • いくつかの特定のエラー ケースに対してのみ、多くのコマンドとクライアントに結合を追加していますか?

最悪の場合、エラーを気にしない多くのコマンドがありますが、1 つまたは 2 つのコマンドは、クライアントが機能したかどうかを知るために重要なことを実行します。チェック済みの例外をインターフェイスに追加すると、すべてのクライアントとすべてのコマンドがエラー処理を実行するようにバインドされ、例外に結合されます。例外をスローしないコマンドのみを処理するクライアントがある場合、コードに大きなオーバーヘッドが生じます。

これはあなたが持ちたくないものです。したがって、エラー処理が必要なコマンドを他のコマンドとは異なるように見えるため、コマンド構造から移動するか、言語で許可されている場合は、気にしてスローするクライアントによってのみ処理されるランタイム例外を追加できます。それらをスローする必要があるコマンド。

もう 1 つの極端な例は、すべてのコマンドが失敗する可能性があり、クライアントがエラーを処理する一貫した方法を持っていることです。これは、エラーが特定のコマンドに依存しないことを意味します。クライアントは、どの種類のコマンドが失敗したかを知る必要はありません。すべてのエラーを同じ方法で処理できます。これで、コマンドのインターフェイスがエラー状態を返すようになり、クライアントはエラーに対処できるようになります。ただし、エラーの処理は、クライアントのコマンドの種類に依存するべきではありません。

2 番目の質問は、コマンドに状態が必要かどうかです。

コマンドが状態を必要とするアーキテクチャと、状態を必要としないアーキテクチャがあります。

これを決定するいくつかの可能性:

  • コマンドを元に戻したい場合は、コマンドに状態が必要です。
  • コマンドがパラメーターの小さなセットで機能する関数を非表示にするためにのみ使用され、結果が状態パターンのように同じコマンドにのみ依存する場合、状態は必要なく、同じオブジェクトを使用できますオーバー。

  • コマンドを使用してスレッド間で通信し、あるスレッドから別のスレッドにデータを転送する場合、コマンドには状態が必要です。

  • ... このリストに入れるべきだと思うものがあれば、コメントを残してください。
于 2009-07-26T19:09:28.403 に答える
7

「ヘッドファーストデザインパターン」を参照します。コマンドパターンに使用する例は次のとおりです。

  1. ダイナーシナリオ(顧客が注文を作成し、ウェイターがキッチンスタッフに叫んで注文を呼び出し、キッチンスタッフが注文を受け取ります)
  2. リモートコントロールのシナリオ(人がボタンをクリックすると、リモートコントロールがコマンドを呼び出し、デバイスがコマンドを受信します)

明らかに最初のケースでは、ある種の状態が受信者によって生成されます。「これが幼虫です」または「ライ麦パンがなくなりました」。高級レストランでは、より高いレベルでの例外処理を通じてこれを行うことができます(メートル・ドテルがテーブルに来て謝罪し、代わりにデザートを提供し、デザートをコンプします)。ウェイターはコマンドを適切に呼び出す以外に何もする必要はありません。しかし、食堂では、料理人が先に進んで茶色のパンを代用するかもしれません。ウェイター(および顧客)は、「ライ麦のマグロはどこにあるのか」とカウンターを見つめることなく、それを処理できる必要があります。これは本で直接取り上げられていませんが、明らかに有効なケースだと思います。

しかし、2番目のシナリオでは、呼び出し元は意図的に愚かにされます。何かが間違っていてもエラーが発生することはなく、まったく効果がありません。すべてのスマートは、コマンドがタイムリーに成功したかどうかを判断するためにクライアントにあり(「がらくた、プラグインするのを忘れた」)、またはレシーバーにあり、何をすべきかを判断します(「CDを再生:CDトレイを閉じる」)。最初")。

私は専門家ではありませんが、一部のアプリケーションでは、呼び出し元にステータスを返すことはまったく問題ありません。

于 2009-07-23T06:18:57.577 に答える
5

とても素敵な議論。私はこの哲学的な質問に何時間も取り組んできましたが、私の執着を満足させる解決策にたどり着きました。(私がこのようなものを気に入っている理由は、具体的なロジックと抽象的なロジック(ブール値+デザイン)を組み合わせているからです。)

結果を返すために例外を使用することを簡単に検討しました。多くの場合、パターン自体の核心であるデカップリングがなくなるため、そのアイデアを放棄しました。さらに、結果は多くの場合、例外ではなく、標準の戻り値になります。おそらく潰瘍になるでしょう。

最終的に、レシーバーをそれ自体でインスタンス化し、それが属するレシーバーのすべてのロジックを保持するクライアントを作成しました。クライアントはコマンドのexecute()を呼び出して続行します。その後、受信者はクライアントのパブリックメソッドを呼び出すことができます。返すものは何もありません。

ここにいくつかのサンプルコードがあります。コマンドクラスがなくても理解できると思うので、コマンドクラスは作成しませんでした。そのexecute()メソッドは、レシーバーのrun()メソッドを呼び出します。

クライアント:

Class ClientType{

    CommandType m_Command;
    ReceiverType m_Receiver;
    boolean m_bResult;

    ClientType(){

      m_Receiver = new ReceiverType(this);
      m_Command = new CommandType(m_Receiver);
    }

    public void run(){  
            ... 
      m_Command.execute();
    }


    /*  Decoupled from both the   
     *  command and the receiver. 
     *  It's just a public function that
     *  can be called from anywhere. /
    public setResult(boolean bResult){
      m_bResult = bResult;
    }
}

受信機:

Class ReceiverType{

    ClientType m_Client;
    boolean m_bResult;

    ReceiverType(ClientType client){
      m_Client = client;
    }

    public void run(){
            ...
      m_Client.setResult(m_bResult);    
    }
}

一見すると、デカップリング要件に違反しているように見えるかもしれません。ただし、クライアントは受信者の実装について何も知らないと考えてください。受信者がクライアントでパブリックメソッドを呼び出すことを知っているという事実は、標準的な料金です。受信者は、パラメータオブジェクトをどう処理するかを常に知っています。依存関係はありません。レシーバーのコンストラクターがClientTypeパラメーターを受け取るという事実は関係ありません。それはどんなオブジェクトでもかまいません。

これは古いスレッドであることは知っていますが、皆さんの何人かがまたチャイムを鳴らしてくれることを願っています。傷が見られたら、遠慮なく私の心を壊してください。それは私たちがやっていることのようなものです。

于 2011-03-26T20:14:55.900 に答える
4

これには議論の余地がありますが、次の 2 つの考え方が明確に示されています。

  • 問題がないか確認してから、次の手順に進みます
  • とにかく進んで、何か悪いことが起こったらそれに対処する

どちらか一方が優れているとは思いません。たとえば Java では、通常、例外処理を乱用せず、手 (および例外) を放り投げる前に起こりうる問題に対処することが最善です。Python では、ステータス コードに関係なく、とにかく先に進んで何でもしようとし、それに応じて例外を単純に処理する方が望ましいです。

コマンド パターンでステータスを返すかどうかは、ユーザー次第です。

于 2009-07-20T17:56:04.660 に答える
4

ここでの問題は、コマンドが実際に何をするかを直接認識していないエグゼキュータ クラスによってコマンドが実行される可能性があります。

戻り値の型を execute メソッドに追加することについて話している場合、実装固有の戻り値の型がエグゼキュータに公開される可能性があります。これは、さまざまなコマンドがさまざまな戻り値のセットを持つ可能性がある状況への扉を開いていることを意味します。エグゼキュータがこれらを処理する必要がある場合、コマンドの実装とより緊密に結合されます。

ただし、コマンドの状態を指定することがよくあります。構築時にクライアントが作業値で構成できるようにし、クライアントが完了時にコマンド実行の結果を抽出できるようにゲッターを提供します。この場合、コマンド パターンに厳密に従わなかった可能性がありますが、設計はうまく機能しました。これについて明確なコードの匂いがしない限り、これは本当に問題なのでしょうか?

注:とはいえ、なぜこれがコードのにおいなのかについての考えを聞きたいです。

于 2009-07-20T18:09:15.723 に答える
3

あなたの質問で言ったように:

コマンドが失敗した場合、呼び出し元のCLIENTはステータスの証明を受け取り、最終的に適切な反応を展開する必要があります。

その場合、必要な情報を含む実行時例外をステータスとしてスローします。あなたはそれを試すことができます。

よろしく、

于 2009-07-26T18:21:29.140 に答える
1

もう 1 つの妥協点は、失敗する可能性のある具体的なコマンドでプロパティ「例外ハンドラー」を公開することです。このようにして、コマンドの作成者は例外を処理でき、クライアントにコードのオーバーヘッドを追加しません。これは、ほとんどのコマンドが失敗してはならない場合に非常に便利です。

于 2009-07-26T21:26:02.230 に答える
0

CAD / CAMソフトウェアでは、コマンドを含むアセンブリは、ソフトウェアのさまざまなUI要素を保持するインターフェイスとオブジェクト階層を含むアセンブリを参照しています。パッシブビューに似ています

コマンドは、ビューインターフェイスを介してUIを操作し、エラー自体を報告できます。

基本的には行きます

フォームはIFormInterfacesを実装し、EXEのScreenViewsに登録します

ScreenObjectsは、IScreenViewを実装し、ScreenViewアセンブリに登録するだけでなく、コマンドアセンブリからコマンドを取得します。

コマンドアセンブリは、ScreenViewアセンブリとモデルを参照します

ScreenView Assemblyは、View Interfaceのコレクションにすぎず、アプリケーションの実装を保持します。

于 2009-07-21T12:51:24.167 に答える