妥協する必要があります:DRY、またはCommand-Query-Separation?


10

私は最近、コマンドとクエリメソッドの両方であるメソッドをリファクタリングしていました。

それを1つのコマンドメソッドと1つのクエリメソッドに分離した後、コマンドを呼び出してクエリから値を取得するコード内の複数の場所があり、これはDRYの原則に違反しているようです。

しかし、その一般的なコードをメソッドにラップすると、そのメソッドはコマンドとクエリの両方になります。これは受け入れられますか?


コミュニティが合意に達しているかどうかはわかりませんでした。また、このトピックに関する議論は見つかりませんでした。
クリスウェールズ

これは、より一般的にCQRSが呼ばれていますgoogle.com.au/...
ダニエル・リトル

@DanielLittle-いいえ、そうではありません。CQSとCQRSは明らかに異なる主題です。CQRSはより複雑なアーキテクチャパターンであり、CQSはよりデザインパターンであり、把握と実装がはるかに簡単です。codebetter.com/gregyoung/2009/08/13/command-query-separation
Erik Funkenbusch

@Erik Funkenbuschあなたね、右
ダニエルリトル

回答:


11

相反する設計原則の間で考慮すべきトレードオフが常にあります。それを解決する方法は、原則の背後にある根本的な理由を見ることです。この場合、コマンドを実行せずにクエリを実行できないことは問題ですが、クエリを実行せずにコマンドを実行できないことは、通常は無害です。クエリをスタンドアロンで実行する方法がある限り、特に次のような場合、クエリ結果をコマンドに追加しない理由はありません。

QueryResult command()
{
   // do command stuff
   return query();
}

4

私は以前にCommand-Query-Separation(CQS)について聞いたことがありませんが、それは単一の責任の原則(SRP)に関連していると思われます。 。

コマンドコードが20行のコードで、クエリコードがさらに30行で、すべてが1つの関数本体にある場合、SRPに違反していることは明らかであり、CQSも想定しているため、これらの2つのロジックは互いに分離する必要があります。

ただし、架空の例では、コードの多くの場所でDRYに違反しないように、コマンドとクエリを組み合わせるラッパーメソッドを作成する可能性が最も高くなります。これもSRP(およびおそらくCQS)違反とは見なしません。これは、ラッパーが責任を1つだけ持っているためです。コマンドをクエリと組み合わせて、より簡単に使用できるより高いレベルの抽象化を作成することです。

ラッパーメソッドは完全に許容できるソリューションだと思います。それを説明するために、例をさらに一歩進めましょう。1つではなく2つのクエリを実行し、それに基づいてコマンドアクションを実行する必要がある場合はどうでしょうか。したがって、2行のコードは6または8になります。一方と他方の間にデータの検証/チェックがあったとすると、15行のコードになります。15行を複数のファイルに振り分けるのではなく、それをすべて行うラッパーを作成することを2度考えますか?


ラッパーの「単一の原則」は、コマンドとクエリを一緒に必要とする他のメソッドをDRYに保つことであるべきだと思います。
Droogans 2013


この問題に対するKarlの解決策はより優れていますが、より長いラッパー関数についての詳細は非常に良い点だと思います。
Kris Welsh

-3

DRYは、より根本的なニーズを解決するため、より重要です。冗長で効果的に無駄な労力を回避します。これは基本的なことです-それを理解するためにプログラマである必要はありません。

CQSは、結果を追跡する効果をサポートしていない言語で、その結果と影響の両方に対して実行されるコードを理解することの困難さへの対応です。しかしながら:

  1. これは小さなユニットから大きなプログラムを作成するための基礎であるため、その結果のためにコードを実行する必要性は避けられません。

  2. 数学や理論的なコンピューターサイエンス以外では、プログラムを実行することの価値は、それが目に見えて私たちに何ができるかによって決まるため、その効果のためにコードを実行する必要性も避けられません。

  3. 同じコードでエフェクトを生成して結果を生成する必要性は避けられません。実際には、どちらか一方だけではなく、エフェクトと構成性の両方が必要だからです。

人間の助けなしでは効果を追跡するのが難しいという問題の実際の解決策は、もちろん、コンピュータで 人間を助けることです!例外とランタイム強制コントラクトが(非)ソリューションを構成するランタイム値(配列インデックスの有効性など)間の複雑な関係の追跡についても、同様のことが言えます。

結論として、CQSのような「ソリューション」は、現実に基づく健全な原則に従ってプログラムを設計することの邪魔になるだけです。DRYに行く。


複雑さを軽減するために、結合を回避する必要がある場合があります。CQRSをご覧ください。
Daniel Little

@Lavinski:複雑さを回避するための最良のツール(それを減らすのではなく、それは無駄です)は抽象化です-解決している問題の一般的な本質を、前述の一般的な問題のインスタンスの特定の詳細から切り離すことです。魔法のレシピ(または、「デザインパターン」と呼ばれているように聞こえます)は、デザインを間違えたときに大きなダメージを与えることを防ぐことができますが、間違ったデザインを正しいものに変えることはできません。
2013

@Lavinski:具体的には、CQRSに関して、概念的に正しい代替ソリューションは、1。データモデルを理解する(オブジェクトレイヤーの量によってこれの必要性を排除できない)、2。データベーススキーマでできるだけ多くの正確性プロパティをエンコードします。(残念ながら、最も人気のあるRDBMSは、後者のサポートがかなり限られています。NoSQLのサポートは言うまでもありません。これにより、さらに問題が発生します。現在の研究では、これに対するより良いソリューションを提供しています。)
2013

CQRSはドメイン駆動設計と完全に一致します。少し調査することをお勧めします。アプリケーション内のドメインは、データストアではなく正確性を適用する必要があります。
Daniel Little

1
@EduardoLeón:デザインが正しいことを証明したい場合は、プログラムのテストを作成してみてください。CQSを破棄しても、その努力が妨げられるだけであることは保証できます。
ステファンビリエット2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.