腐敗防止レイヤーが実際に何を意味するのかを理解しようとしています。レガシーコードや不正なAPIを移行または回避する方法であることは知っています。私が理解していないのは、それがどのように機能するか、そして望ましくない層からそれをきれいに分離するものです。
検索はしましたが、簡単な例や説明が見つからないので、それを理解して簡単な例で説明してくれる人を探しています。私の質問を満足させる答えは単純で(必ずしも短くはない)、実装と使用の理解できる例を提供する必要があります。
私の使用例については、この 質問 を参照してください。
以下に示すように設計された他の誰かのコードを使用しなければならないことを想像してください:
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
のものを直接使用します。
上記の例は Anticorruption Layer がc2 wikiでどのように説明されているかに基づいています:
アプリケーションがデータベースまたはモデルが望ましくないか、独自のアプリケーション内の必要なモデルに適用できない別のアプリケーションを処理する必要がある場合は、AnticorruptionLayerを使用して、そのモデルとユーザーのモデルの間で変換を行います。
注の例は、説明を簡潔にするために意図的に単純化され、凝縮されています。
破損防止層の背後にある大きなmess-of-APIがある場合、同じアプローチが適用されます。最初に、「メインコード」が破損したものにアクセスしないようにします直接および2番目に、使用状況でより便利な方法で公開します。
上記の簡単な例を超えてレイヤーを「スケーリング」する場合、APIを便利にすることは必ずしも簡単な作業ではないことを考慮してください。 努力 から レイヤーを適切に設計 に投資し、 単体テスト などを使用してその使用目的を確認します。
つまり、APIが実際に非表示になっているものよりも改善であることを確認し、破損の別のレイヤーを導入するだけではないことを確認してください。
完全を期すために、これと関連するパターン Adapter と Facade の間の微妙ですが重要な違いに注意してください。その名前で示されているように、腐敗防止レイヤーは、根底にあるAPIが 品質の問題 (「壊れている」)であると想定し、言及された問題の保護を提供するつもりです。
このように考えることができます。ライブラリデザイナーがReasonable
ではなくMessy
を使用してその機能を公開するほうがよいと正当化できる場合、これは腐敗防止レイヤーに取り組んでいることを意味します。 彼らの仕事、彼らの設計ミスの修正。
それとは対照的に、AdapterおよびFacadeは、基礎となる設計の品質を想定していません。これらは、最初からうまく設計されているAPIに適用でき、特定のニーズに合わせて調整するだけです。
実際には、AdapterやFacadeのようなパターンは、基礎となるコードが適切に設計されていることを期待していると想定すると、さらに生産的になる可能性があります。このように考えることができます。適切に設計されたコードは、特定のユースケースでTweakするのが難しくないはずです。アダプターの設計に予想以上の労力がかかることが判明した場合、これは、基礎となるコードがなんらかの理由で「破損」していることを示している可能性があります。その場合は、ジョブを分割してフェーズを分けることを検討できます。まず、破損防止レイヤーを確立して、基盤となるAPIを適切に構造化された方法で提示し、次に、その保護レイヤーの上にアダプター/ファサードを設計します。
別の出典を引用するには:
分離層を作成して、クライアントに独自のドメインモデルの機能を提供します。レイヤーは、既存のインターフェースを介して他のシステムと通信します。他のシステムをほとんどまたはまったく変更する必要はありません。内部的には、レイヤーは2つのモデル間で必要に応じて両方向に移動します。
Eric Evans、ドメイン駆動設計、16番目の印刷、365ページ
最も重要なことは、不正防止レイヤーの両側で異なる用語が使用されていることです。私はかつて輸送物流のシステムに取り組んでいました。ラウンドを計画する必要がありました。車両をデポに装備し、さまざまな顧客サイトに車で行き、それらにサービスを提供し、タンクストップなどの他の場所を訪問する必要がありました。しかし、より高いレベルから、これはすべてタスク計画に関するものでした。したがって、より一般的なタスク計画の用語を、非常に具体的な輸送ロジスティクスの用語から分離することは理にかなっています。
したがって、破損防止レイヤーの分離は、厄介なコードからユーザーを保護するだけではなく、異なるドメインを分離し、将来的にそれらが分離されたままであることを確認することです。
アダプター
互換性のないインターフェースがある場合、同様のロジックを実行して、一方を他方に適応させ、一方の実装を、他方を期待するもので使用できるようにします。
例:
Carを必要とするオブジェクトがありますが、4WheelVehicleクラスしかないため、CarBuiltUsing4WheelVehicleを作成し、それをCarとして使用します。
ファサード
複雑/混乱/巨大なAPIがあり、それをより単純/明確/小さくしたい場合。ファサードを作成して、複雑さ/混乱/エクストラを隠し、新しいシンプル/クリア/スモールAPIのみを公開します。
例:
あなたは100のメソッドを持つライブラリを使用しており、特定のタスクを実行するには、一連の初期化、接続、開閉を行い、最終的に必要なことを実行できるようにする必要があります。ライブラリーが実行できる50のすべて、つまり、必要な1つの機能のメソッドのみを持ち、初期化、クリーニングなどをすべて行うFacadeを作成します。
腐敗防止レイヤー
ドメイン外のシステムがある場合でも、ビジネスニーズにより、その別のドメインで作業する必要があります。この他のドメインを自分のドメインに導入したくないので、ドメインを破壊したいので、ドメインの概念をこの他のドメインに、またはその逆に変換します。
例:
1つのシステムは、トランザクションごとに1つずつ、名前と文字列のリストを持つ顧客を表示します。プロファイルは名前を持つスタンドアロンクラスと見なされ、トランザクションは文字列を持つスタンドアロンクラスと見なされ、顧客はプロファイルとトランザクションのコレクションを持つものと見なされます。
そのため、お客様と他のシステムのお客様の間で変換できるようにするACLレイヤーを作成します。この方法では、他のシステムのCustomerを使用する必要はなく、ACLに通知するだけです。プロファイルXの顧客.
====================
これらはすべて間接パターンであるため、3つすべては比較的似ています。しかし、それらは異なる構造、クラス/オブジェクト、API、モジュール/サブシステムを扱います。必要に応じて、すべてを組み合わせることができます。サブシステムには複雑なAPIがあるため、FACADEを作成し、異なるモデルを使用するため、モデルに適合しないデータ表現ごとに、そのデータをモデル化する方法に変換します。最後に、インターフェイスも互換性がない可能性があるため、ADAPTERSを使用して一方から他方に適応させます。
ここでの多くの回答は、ACLは乱雑なコードのラップに関する「単なる」ものではないと述べています。私はさらに進んで、彼らはまったくそれについてではないと言います、そして、彼らがそうするならば、それは副次的な利益です。
腐敗防止層とは、あるドメインを別のドメインにマッピングすることです。これにより、2番目のドメインを使用するサービスは、最初のドメインの概念によって「腐敗」する必要がなくなります。 ACLはドメインモデルに対するものであり、クラスに対するアダプタとは何か、それは単に異なるレベルで発生しているだけです。アダプターは間違いなく最も重要な設計パターンです-私はいつもそれを使用しています-ラップされたクラスが乱雑であるかどうかを判断することは無関係です。それが何であるか、私はそれが別のインターフェースを持っていることを必要とするだけです。
乱雑さに焦点を当てることは誤解を招きやすく、DDDが何であるかという要点を逃します。 ACLは、品質の低下ではなく、概念的な不一致を処理することを目的としています。