web-dev-qa-db-ja.com

コマンドパターンと機能分解

Android開発者はおそらく Ceja's Clean Architecture に精通しており、ユースケースは Command Pattern を実装するクラスです。

Shvetsは、パターンインテントを次のように定義します。

  • リクエストをオブジェクトとしてカプセル化します。これにより、さまざまなリクエストでクライアントをパラメータ化し、リクエストをキューまたはログに記録して、取り消し可能な操作をサポートできます。
  • 「オブジェクトのメソッドの呼び出し」を完全なオブジェクトステータスに昇格させる
  • オブジェクト指向のコールバック

私はコードの可読性とテスト容易性を向上させるためにそのアプローチを使用しています。しかし、Shvetsの Anti-Patternsコース を読んだ後、私は彼の Functional Decomposition Anti-Pattern 定義と混同しました:

  • Calculate_InterestやDisplay_Tableなどの「関数」名を持つクラスは、このAntiPatternの存在を示している可能性があります。
  • すべてのクラス属性はプライベートであり、クラス内でのみ使用されます。
  • 関数などの単一のアクションを持つクラス
  • オブジェクト指向アーキテクチャーのポイントを完全に逃した、信じられないほど縮退したアーキテクチャー。
  • 継承やポリモーフィズムなどのオブジェクト指向の原則の活用は絶対にありません。これは、維持するのに非常に費用がかかる可能性があります(そもそもそれが機能したとしても、テクノロジーへの競争を徐々に失っていく古いプログラマーの創意工夫を過小評価しないでください)。
  • システムがどのように機能するかを明確に文書化する(または説明する)方法はありません。クラスモデルはまったく意味がありません。
  • ソフトウェアの再利用を期待することはできません。
  • テスター側の欲求不満と絶望。

Command Pattern の代わりに Functional Decomposition Anti-Pattern を使用している場合、どうすればわかりますか?

5
JP Ventura

ここでの主な問題はShvetsのアンチパターンの説明に欠陥があると思います。それが、コマンドパターンと区別するのに問題がある理由です。

  1. この名前は、属性「アンチパターン」とともに古い既存の用語「機能分解」を使用しているため、欠陥があります。

    この用語は、以前は「パターン」または「アンチパターン」の概念に関連付けられていなかったAFAIKでしたが、彼のトピックのためにそれを選択することにより、Shvetsはそれに新しい、混乱する意味を与えました。プログラミングにおける本来の意味は「 複雑なプロセスを取り、それをより単純な部分に分解する 」であり、それが今日でもこの用語を使用する方法です。 ( Wikipediaの定義 は数学におけるより一般的な用語についてですが、これはおそらく私たちのプログラミングのコンテキストではあまり役に立たないと思います。)参照 この以前のSEの投稿 、他の人がアンチパターン名で既に問題を抱えていた場合、上の答えは代わりに「手続き分解」という名前を提案していますが、その名前は同様の問題を引き起こす可能性があります。

  2. パターンの説明から引用した最初の3つの症状は、実際にはコマンドオブジェクトに適していますが、他の適切に設計されたクラスにも適しています。それがまさに、彼らが私見である理由です(= /// =)Shvetsが説明するこの種の「縮退したアーキテクチャ」の良い指標ではありません。たとえば、「 関数として単一のアクションを持つクラス 」に関するこの以前のSEの投稿を参照してください。

Shvetsがおそらく頭に浮かんだのは、人々が手続き型コードをクラスにシューホーンしようとする状況でした。これは、ほとんどの場合、典型的なコマンドオブジェクトのスケールとは完全に異なるスケール( "アプリケーションスケール"について言及)で発生します。インスタンス化された「オブジェクト」は「モジュール」または「サブ」以外の抽象化ではないため、これは、SOLIDではないコードを生成し、クラスが大きすぎ、適切な抽象化が欠落し、ほとんどの場合すべてが静的である可能性があるクラスになります。 -program」、一度だけ存在し、アプリケーションプロセス全体と同じ存続期間を持ちます。

ただし、コマンドオブジェクトは、プログラミング言語が高次関数またはクロージャの組み込みメカニズムを提供しない状況では、よりオブジェクト指向の回避策です。 「コマンドパターン」という用語は、C++とJava=が支配的であった時代であり、これらの言語の両方がそのようなメカニズムを提供していなかった時代である1995年のGOFの「デザインパターン」の本にその起源がありますそのため、独自の抽象化としてプログラム内で関数を渡す必要がある場合は、おそらくコマンドパターンを使用するのが最善の選択でした。

現在、これらの両方の言語(および他の最新の言語)は、データ型に関数をカプセル化するための追加のメカニズムを提供しているため、(元のOO = form)more。それでも、「コマンド」がカプセル化する「アクション」または「関数」だけでなく、より複雑なインターフェースが必要な場合にも役立ちます。

8
Doc Brown

あなたが違いを言う方法は、機能分解アンチパターンの男がそれを使っている無差別にに対し、クリーンアーキテクチャの男はそれを使っている- 実際に役立つ目的がある場合

コマンドパターンは、オブジェクトを使用してアクションを実行したり、後でイベントをトリガーしたりするために必要なすべての情報をカプセル化する動作設計パターンです。この情報には、メソッド名、メソッドを所有するオブジェクト、およびメソッドパラメータの値が含まれます。

関数分解の担当者は、関数を含める以外の目的ではないクラスコンテナーで関数を呼び出すことを優先して、オブジェクトのすべての利点を破棄しました。

IComparable Interface も見てください。これには 単一のメソッド もあります。

3
Robert Harvey

コマンドパターンを適切に使用すると、コマンドを選択したときと実行するときの時間または場所が異なります。キュー、コールバック、元に戻すスタックなどに入れられます。

基本的に他に選択の余地がないため、機能分解アンチパターンはメソッドをクラスに配置するだけです。言語またはコードレビューによって強制されますが、コマンドを選択して実行する時間と場所の分離を最小限に抑えたいと考えています。関数を配置するボックスが必要なかった場合は、クラスをまったく使用しません。

3
Karl Bielefeldt

コマンドパターンは、Shvetsの機能分解アンチパターン(FDAP)にも、FDAPに提案されたより優れた方法(抽象化)にもないものをエンコードしています。

コマンドは、CalculateInterestDisplayTableのような1つの特定のアクションだけでなく、アクションの一般的な、アクションのabstractionについても推論する手段です。それはプレイ時の具現化です。アクションは、実行できるオブジェクトになるだけでなく、実行スタックに渡して、実行する意味があり、どこかに保存し、メタアクション(undoredotrace)など.

多くのFDAP症状(継承/ポリモーフィズムの活用なし、再利用なし)になりにくいことに加えて、コマンドはとにかくそれを打ち負かします。なぜなら、それは通常、そのアンチパターンによってターゲットとされるクラスの種類とはまったく異なる抽象化レベルだからです。

2
guillaume31

メタファーは本質的に漏れやすい抽象化ですが、私の思考スタイルは本質的にメタファー主導なので、この質問に対する私のアプローチを次に示します。


メタファー1-複数部門組織の各部門の担当者(または「プロジェクトリーダー」)。

説明

  • 特定のタスクを部門で達成するには、特定の連絡担当者に特定のタスクについて伝え、その部門にその人の努力を組織させます。

分析

  • コマンドパターンは、「その担当者に伝える」ことをカプセル化します。

メタファー2-有名な「入力、処理、出力」ブラックボックス。

説明

  • 開始者は、プロセスに必要なすべての入力を提供します。
  • イニシエーターはプロセスを開始させます。
  • プロセスの実行中は、イニシエーターと実行中のプロセスの間で対話や介入を行う機会はありません。
  • プロセスが終了すると、イニシエーターは出力を取得するか、障害を確認します。

分析

  • このアナロジーでは、それはオブジェクト指向と手続き型プログラミングの間の境界線の選択です。
  • 入力、プロセス自体、および出力がすべて単純である場合は、オブジェクトから関数にダウングレードすることを検討する価値があります。
  • ただし、次のように3つの項目(入力、プロセス、出力)のいずれかが重要な場合は、
    (i)それらの数が多い、または
    (ii)多くの内部状態または多くの内部ステップを含むプロセス)、
    それでも、このプロセスを関数からオブジェクトに昇格させることには価値があります。
  • この場合、再利用の単位はプロセス自体です。

(警告:動物虐待の警告)


Metaphor 3-いきつくり

説明

  • 生物は生きたまま、細かくスライスされます。
  • 生物の差し迫った死は避けられません。
  • 生物を断片にスライスするプロセスは、多くの重要な臓器や組織を破壊します。
  • 血管と毛細血管を検討してください。それらは、生物の周りに栄養素(インプット)と廃棄物(アウトプット)を運びます。血管の破壊は、生物の生命を維持することができないことを意味します。
  • 個々の内臓も部分にスライスされます。
  • 各臓器の内部構造も部分にスライスされます。

分析

  • 内部で多くの状態を必要とする機能、入力と出力はいくつかの部分に分解されます。
  • これらの部品を単独で再利用する可能性はありません。
  • 機能を実現するには、状態、入力、および出力がこれらのパーツ間で何らかの形で流れる必要があります。
  • 多くの場合、この情報フローは、手続き型プログラミングのコンテキストでは、グローバル状態(より具体的には「グローバルに共有される可変状態」)として発生します。

メタファー4-臓器に損傷がない生き物

説明

  • この生き物は生きたままスライスされる運命を免れています。
  • この生物の内臓は、循環系や神経系などの広大なネットワークでつながっています。

分析

  • 機能分解アンチパターンのアーティファクトが適切にカプセル化されると、アンチパターンとは見なされなくなります。
  • 多くの場合、このカプセル化プロセスの結果、オブジェクト指向の設計を使用して、パーツがオブジェクトに再統合されます。
  • 生物(問題のソフトウェアシステム)がすでにしばらくの間死んでいる場合、カプセル化(ソフトウェアプロジェクトの死からの脱出)は実行可能なオプションではなくなりました。

(免責事項:この比喩のリストを作成する過程でいきいきは発生しません。)

1
rwong

パターンとアンチパターンの間には客観的な区別がないことに注意してください。アンチパターンは、コードを改善しない状況で適用される単なるパターンです。

「機能分解」は、協調オブジェクトとしてではなく、一連の関数呼び出しとして設計されたアーキテクチャを表します。これは、単一の関数をカプセル化するコマンドクラスにつながる可能性があります(それらはオブジェクトではなく関数と見なされ、凝集と結合を適切に考慮せずにクラスにラップされるだけです)、または適切な分離なしに100万の関数を持つ神クラスにつながる可能性があります懸念の。

コマンドパターンを、実際にはコマンドパターンを必要としないコンテキストで使用すると、関数分解アンチパターンに陥る可能性があります。

(アンチパターンは悪いコードを意味しないことにも注意してください。それは単に悪いオブジェクト指向設計を意味します。関数型言語は、関数分解が非常に強力なパラダイムであることを示しています。それはオブジェクト指向設計。)

1
JacquesB