回答:
以下に示すように設計された他の誰かのコードを使用する必要があると想像してください。
class Messy {
String concat(String param, String str) { /* ... */ }
boolean contains(String param, String s) { /* ... */ }
boolean isEmpty(String param) { /* ... */ }
boolean matches(String param, String regex) { /* ... */ }
boolean startsWith(String param, String prefix) { /* ... */ }
}
ここで、それに依存するコードが次のようになっていることがわかると想像してください。
String process(String param) {
Messy messy = new Messy();
if (messy.contains(param, "whatever")) {
return messy.concat(param, "-contains");
}
if (messy.isEmpty(param)) {
return messy.concat(param, "-empty");
}
if (messy.matches(param, "[whatever]")) {
return messy.concat(param, "-matches");
}
if (messy.startsWith(param, "whatever")) {
return messy.concat(param, "-startsWith");
}
return messy.concat(param, "-whatever");
// WTF do I really need to repeat bloody "param" 9 times above?
}
...そして、特に、アプリケーションに必要のないパラメータの繰り返し使用を取り除くために、使いやすくしたいこと。
さて、腐敗防止レイヤーの構築を開始します。
最初のことは、「メインコード」がMessy
直接参照しないようにすることです。たとえば、アクセスしようとするとコンパイルに失敗するような方法で依存関係管理を調整Messy
します。
次に、あなただけがアクセスできる専用の「レイヤー」モジュールを作成し、Messy
それを「メインコード」に公開します。
レイヤーコードは次のようになります。
class Reasonable { // anti-corruption layer
String param;
Messy messy = new Messy();
Reasonable(String param) {
this.param = param;
}
String concat(String str) { return messy.concat(param, str); }
boolean contains(String s) { return messy.contains(param, s); }
boolean isEmpty() { return messy.isEmpty(param); }
boolean matches(String regex) { return messy.matches(param, regex); }
boolean startsWith(String prefix) { return messy.startsWith(param, prefix); }
}
その結果、「メインコード」はを混乱させずMessy
、Reasonable
代わりに次のように使用します。
String process(String param) {
Reasonable reasonable = new Reasonable(param);
// single use of "param" above and voila, you're free
if (reasonable.contains("whatever")) {
return reasonable.concat("-contains");
}
if (reasonable.isEmpty()) {
return reasonable.concat("-empty");
}
if (reasonable.matches("[whatever]")) {
return reasonable.concat("-matches");
}
if (reasonable.startsWith("whatever")) {
return reasonable.concat("-startsWith");
}
return reasonable.concat("-whatever");
}
まだ少し混乱していることに注意してください。Messy
しかし、これは今やかなり奥深くに隠されReasonable
ており、「メインコード」を合理的にきれいにし、直接的な使用によってもたらされる破損がないようにしMessy
ます。
上記の例は、c2 wikiでAnticorruption Layerがどのように説明されているかに基づいています。
アプリケーションが、モデルが望ましくない、または自分のアプリケーション内のモデルに適用できないデータベースまたは別のアプリケーションを処理する必要がある場合は、AnticorruptionLayerを使用して、そのモデルと自分のモデル間で変換します。
注の例は、説明を簡潔にするために、意図的に単純化および要約されています。
腐敗防止レイヤーの背後にあるより大きなAPIを使用している場合、同じアプローチが適用されます。まず、「メインコード」が破損したものに直接アクセスしないことを確認し、次に、より適切な方法で公開します使用状況で便利です。
上記の簡単な例を超えてレイヤーを「スケーリング」する場合、APIを便利にすることは必ずしも簡単な作業ではないことを考慮してください。レイヤーを正しい方法で設計するための努力を投資し、単体テストなどでその使用目的を検証します。
言い換えれば、APIが実際に隠されているAPIよりも改善されていることを確認し、破損の別のレイヤーを導入するだけではないことを確認してください。
完全を期すために、このパターンと関連パターンAdapterとFacadeの微妙ではあるが重要な違いに注意してください。その名前が示すように、腐敗防止レイヤーは、基礎となる APIに品質の問題がある(「破損している」)と想定し、言及された問題の保護を提供する予定です。
このように考えることができます:ライブラリデザイナーがのReasonable
代わりにその機能を公開した方が良いと正当化できる場合Messy
、これは腐敗防止レイヤーに取り組んでおり、彼らの仕事をして、設計ミスを修正していることを意味します。
それとは対照的に、AdapterおよびFacadeは、基礎となる設計の品質について仮定を行いません。これらは、最初から適切に設計されたAPIに適用でき、特定のニーズに合わせて調整できます。
実際、AdapterやFacadeなどのパターンは、基礎となるコードが適切に設計されていることを前提としていると仮定する方が生産的です。このように考えることができます。適切に設計されたコードは、特定のユースケースに合わせて微調整するのが難しくないはずです。アダプターの設計が予想よりも多くの労力を要することが判明した場合、これは、基礎となるコードが何らかの形で「破損している」ことを示している可能性があります。その場合、ジョブを複数のフェーズに分割することを検討できます。まず、適切に構造化された方法で基礎となるAPIを提示する腐敗防止レイヤーを確立し、次にその保護レイヤー上でアダプター/ファサードを設計します。
In other words, make sure that your API is indeed an improvement over one it hides, make sure that you don't just introduce another layer of corruption.
そのセクション全体は、太字のタグに値します。
別のソースを引用するには:
分離層を作成して、クライアントに独自のドメインモデルの機能を提供します。レイヤーは既存のインターフェースを介して他のシステムと通信し、他のシステムへの変更はほとんどまたはまったく必要ありません。内部的に、レイヤーは2つのモデル間で必要に応じて両方向に移動します。
Eric Evans、ドメイン駆動設計、第16回印刷、365ページ
最も重要なことは、腐敗防止レイヤーの両側で異なる用語が使用されることです。私はかつて輸送物流のシステムに取り組んでいました。ラウンドを計画する必要がありました。車両をデポに装備し、さまざまな顧客サイトに運転してサービスを提供し、タンク停留所などの別の場所を訪問する必要がありました。しかし、より高いレベルからは、これはすべてタスク計画に関するものでした。したがって、より一般的なタスクプランニングの用語を、非常に具体的な輸送ロジスティック用語から分離することは理にかなっています。
したがって、破損防止レイヤーの分離は、面倒なコードからユーザーを保護することだけでなく、異なるドメインを分離し、それらが将来も分離された状態を維持するようにすることです。
アダプタ
互換性のないインターフェイスがある場合、同様のロジックを実行して、一方を他方に適応させ、一方の実装を他方を期待するものと使用できるようにします。
例:
Carを必要とするオブジェクトはありますが、4WheelVehicleクラスしか持っていないため、CarBuiltUsing4WheelVehicleを作成し、それをCarとして使用します。
ファサード
複雑/混乱/巨大なAPIがあり、それをより簡単/明確/小さくしたい場合。Facadeを作成して、複雑さ/混乱/エクストラを隠し、新しいシンプル/クリア/スモールAPIのみを公開します。
例:
100個のメソッドを持つライブラリを使用しており、特定のタスクを実行するには、一連の初期化、接続、オープン/クローズを実行する必要があります。ライブラリはすべて50を実行できるため、必要な1つの機能のメソッドのみを持ち、すべての初期化、クリーニングなどを行うFacadeを作成します。
腐敗防止レイヤー
ドメイン外のシステムを使用している場合でも、ビジネス上のニーズにより、他のドメインと連携する必要があります。この別のドメインを自分のドメインに導入したくないため、破損しているので、ドメインの概念をこの別のドメインに、またはその逆に変換します。
例:
1つのシステムは、トランザクションごとに1つの名前と文字列のリストを持つ顧客を表示します。プロファイルは名前を持つスタンドアロンクラス、トランザクションは文字列を持つスタンドアロンクラス、顧客はプロファイルとトランザクションのコレクションを持つとみなします。
そのため、お客様と他のシステムのお客様との間で翻訳できるACLレイヤーを作成します。このように、他のシステムの顧客を使用する必要はありません。ACLを伝える必要があります。「プロファイルXで顧客を提供してください。あなたはプロファイルXの顧客です。
=====================
これらはすべて間接パターンであるため、3つとも比較的似ています。しかし、それらは異なる構造、クラス/オブジェクト対API対モジュール/サブシステムに対処します。必要に応じて、それらをすべて組み合わせることができます。サブシステムには複雑なAPIがあるため、そのためにFACADEを構築し、異なるモデルを使用するため、モデルに適合しない各データ表現に対して、そのデータを元のモデルに変換します。最後に、インターフェイスにも互換性がない可能性があるため、ADAPTERSを使用して一方から他方に適応します。
ここでの回答の多くは、ACLは乱雑なコードをラップすることだけではないということです。私はさらに進んで、彼らはまったくそれについてではないと言うでしょう、そして彼らがそうするならば、それは副次的な利益です。
腐敗防止レイヤーとは、あるドメインを別のドメインにマッピングすることで、2番目のドメインを使用するサービスが最初のドメインの概念によって「破損」する必要がないようにすることです。ACLはドメインモデルに対するものであり、アダプターはクラスに対するものであり、異なるレベルで発生しているだけです。アダプタは間違いなく最も重要なデザインパターンです-私は常にそれを使用します-しかし、ラップされたクラスが乱雑であるかどうかを判断することは無関係です。それが何であるか、別のインターフェイスを持っている必要があります。
乱雑さに焦点を当てることは誤解を招くものであり、DDDが何であるかという点を見逃しています。ACLは、品質の低下ではなく、概念的な不一致の処理に関するものです。