Yan Hon 博士の著書「JAVA とパターン」は、メディエーター パターンの説明で始まります。
メディエーター パターンはオブジェクトの動作パターンです。 Mediator パターンは、一連のオブジェクトが相互に相互に明示的に参照する必要がないように相互作用する方法をまとめています。これにより、それらを疎結合にすることができます。これらのオブジェクト間の相互作用が変化しても、他のオブジェクト間の相互作用にはすぐには影響しません。これにより、これらの相互作用が互いに独立して変化することが保証されます。
なぜ仲介者が必要なのか
以下の図に示すように、このスケマティック ダイアグラムには多数のオブジェクトがあり、これらのオブジェクトは他のオブジェクトに影響を与えることも、他のオブジェクトから影響を受けることもできるため、多くの場合同僚オブジェクトと呼ばれます。これらの同僚は、相互作用を通じてシステムの動作を形成します。図からわかるように、ほとんどすべてのオブジェクトは他のオブジェクトと相互作用する必要があり、この相互作用は、あるオブジェクトと別のオブジェクトの間の直接的な結合として現れます。これは過結合システムです。
メディエーターオブジェクト(Mediator)を導入することで、下図に示すように、システムのネットワーク構造をメディエーターを中心としたスター型構造に変換することができます。このスター構造では、同僚オブジェクトは直接接続を通じて別のオブジェクトと対話するのではなく、メディエーター オブジェクトを通じて別のオブジェクトと対話します。メディエーター オブジェクトの存在により、オブジェクト構造の安定性が保証されます。つまり、新しいオブジェクトの導入によってシステム構造に多くの変更作業が発生することはありません。
優れたオブジェクト指向設計により、コラボレーションが強化され、オブジェクト間の結合が軽減されます。よく考えられた設計では、システムを協力するコワーカー オブジェクトのグループに分割し、各コワーカー オブジェクトに固有の責任を与え、それらが連携して動作できるようにオブジェクト間の協力関係を適切に構成します。
マザーボードがない場合
ご存知のとおり、コンピューター内のさまざまなアクセサリ間の相互作用は主にマザーボードを通じて行われます。コンピューターにマザーボードがない場合は、さまざまなアクセサリが独自に相互作用してデータを相互に送信する必要があります。また、各アクセサリのインターフェイスは異なるため、相互に通信する場合は、データ インターフェイスを一致するように変換する必要があります。
幸いなことに、マザーボードを使用すると、さまざまなアクセサリの相互作用はマザーボードを通じて完全に完了し、各アクセサリはマザーボードと相互作用するだけでよく、マザーボードはすべてのアクセサリの処理方法を知っているため、処理がはるかに簡単になります。
メディエーターパターンの構造
Mediator パターンの概略クラス図を以下に示します。
メディエーター モードには次の役割が含まれます。
● 抽象メディエーター (Mediator) ロール:同僚オブジェクトからメディエーター オブジェクトへのインターフェイスを定義します。メイン メソッドは 1 つ (または複数) のイベント メソッドです。
● 具体的メディエーター (ConcreteMediator) ロール: 抽象メディエーターによって宣言されたイベント メソッドを実装します。特定のメディエータは、すべての特定の同僚クラスを認識しており、各同僚オブジェクト間の対話を具体的に調整する責任を負います。
● 抽象同僚クラス (Colleague) ロール: メディエーターから同僚オブジェクトへのインターフェースを定義します。同僚オブジェクトはメディエータについてのみ知っており、他の同僚オブジェクトについては知りません。
● ConcreteColleague ロール: すべての具体的な同僚クラスは、抽象的な同僚クラスを継承します。自分のビジネスを実行するために、他の同僚と連絡を取る必要がある場合は、それを保持する仲介者と連絡します。仲介者は他の同僚とのやり取りを担当します。
ソースコード
抽象メディエーター クラスのコピー コードは次のとおりです。
パブリック インターフェイス メディエーター {
/**
* 同僚オブジェクトは、変更されるとメディエーター メソッドに通知します。
* メディエーターに他の同僚オブジェクトとの対応する対話を担当させます
*/
public void が変更されました(同僚 c);
}
具象メディエータークラス
次のようにコードをコピーします。
public class ConcreteMediator は Mediator を実装します {
//同僚 A を保持および維持する
プライベートコンクリート同僚A同僚A;
//同僚 B を保持および維持する
プライベートコンクリート同僚B同僚B;
public void setColleagueA(ConcreteColleagueA 同僚A) {
this.colleagueA = 同僚A;
}
public void setColleagueB(ConcreteColleagueB 同僚B) {
this.colleagueB = 同僚B;
}
@オーバーライド
public void selected(Colleague c) {
/**
* 特定の同僚のクラスが変更され、通常は他の同僚とのやり取りが必要になります
* 対応する同僚オブジェクトを具体的に調整して、協力的な動作を実現します
*/
}
}
抽象的な同僚クラス
次のようにコードをコピーします。
パブリック抽象クラス 同僚 {
//メディエーターオブジェクトを保持する
プライベートメディエーター メディエーター。
/**
*コンストラクタ
*/
public Colleague(調停者 調停者){
this.mediator = メディエーター;
}
/**
* 現在の同僚クラスに対応するメディエーター オブジェクトを取得します
*/
public Mediator getMediator() {
仲介者を返す。
}
}
同じクラスをコピーするための具体的なコードは次のとおりです。
public class ConcreteColleagueA extends Colleague {
public ConcreteColleagueA(Mediator メディエーター) {
スーパー(メディエーター);
}
/**
* 特定の操作を実行する方法を示します
*/
パブリック void オペレーション(){
//他の同僚と通信する必要がある場合にメディエーター オブジェクトに通知します
getMediator().changed(this);
}
}
次のようにコードをコピーします。
public class ConcreteColleagueB extends Colleague {
public ConcreteColleagueB(Mediator メディエーター) {
スーパー(メディエーター);
}
/**
* 特定の操作を実行する方法を示します
*/
パブリック void オペレーション(){
//他の同僚と通信する必要がある場合にメディエーター オブジェクトに通知します
getMediator().changed(this);
}
}
コンピュータを使って映画を見る
日常生活では、コンピューターを使用して映画を鑑賞することがよくありますが、このプロセスを単純化して説明すると、次のようなインタラクティブなプロセスがあると仮定します。
(1) まず、光ドライブは光ディスク上のデータを読み取り、ステータスが変化したことをマザーボードに伝える必要があります。
(2) マザーボードは光学ドライブからデータを取得し、分析と処理のためにそのデータを CPU に渡します。
(3) CPU は処理終了後、映像データと音声データに分割し、マザーボードに処理終了を通知します。
(4) マザーボードは、CPU によって処理されたデータを取得し、そのデータをグラフィックス カードとサウンド カードにそれぞれ渡し、ビデオを表示したり音声を出したりします。
メディエーター パターンを使用して例を実装するには、同僚オブジェクトとメディエーター オブジェクトを区別する必要があります。明らかに、マザーボードが仲介者であり、光学ドライブ、サウンド カード、CPU、グラフィックス カードなどのアクセサリはすべて同僚です。
ソースコード
抽象同僚クラスのコピー コードは次のとおりです。
パブリック抽象クラス 同僚 {
//メディエーターオブジェクトを保持する
プライベートメディエーター メディエーター。
/**
*コンストラクタ
*/
public Colleague(調停者 調停者){
this.mediator = メディエーター;
}
/**
* 現在の同僚クラスに対応するメディエーター オブジェクトを取得します
*/
public Mediator getMediator() {
仲介者を返す。
}
}
同僚クラス - 光学ドライブのコピー コードは次のとおりです。
パブリック クラス CDDriver extends Colleague{
//光学ドライブから読み取られたデータ
プライベート文字列データ = "";
/**
*コンストラクタ
*/
public CDDriver(Mediator メディエーター) {
スーパー(メディエーター);
}
/**
* ディスクから読み取ったデータを取得します
*/
public String getData() {
データを返す。
}
/**
*CDを読む
*/
public void readCD(){
//カンマの前はビデオに表示されるデータ、カンマの後は音声です
this.data = "ワンピース、海賊王に俺は決まった";
//ステータスが変化したことをマザーボードに通知します
getMediator().changed(this);
}
}
同僚クラス——CPU
次のようにコードをコピーします。
パブリック クラス CPU は Colleague { を拡張します
// 分解されたビデオデータ
プライベート文字列ビデオデータ = "";
// 分解した音声データ
private String soundData = "";
/**
*コンストラクタ
*/
パブリック CPU(Mediator メディエーター) {
スーパー(メディエーター);
}
/**
* 分解されたビデオデータを取得します
*/
public String getVideoData() {
videoData を返します。
}
/**
* 分解された音声データを取得します
*/
public String getSoundData() {
サウンドデータを返します。
}
/**
※データを加工して音声データと映像データに分割します
*/
public voidexecuteData(文字列データ){
//データを分解します。前がビデオデータ、後ろがオーディオデータです。
String[] 配列 = data.split(",");
this.videoData = 配列[0];
this.soundData = 配列[1];
//CPU が作業を完了したことをマザーボードに通知します
getMediator().changed(this);
}
}
同僚クラス - グラフィックス カードのコピー コードは次のとおりです。
public class VideoCard extends Colleague {
/**
*コンストラクタ
*/
public VideoCard(Mediator メディエーター) {
スーパー(メディエーター);
}
/**
* ビデオデータを表示
*/
public void showData(文字列データ){
System.out.println("あなたは見ています: " + data);
}
}
同僚クラス - サウンド カードのコピー コード コードは次のとおりです。
public class SoundCard extends Colleague {
/**
*コンストラクタ
*/
public SoundCard(Mediator メディエーター) {
スーパー(メディエーター);
}
/**
* オーディオデータを元に音を出します
*/
public void soundData(文字列データ){
System.out.println("ナレーション: " + データ);
}
}
抽象メディエーター クラスのコピー コードは次のとおりです。
パブリック インターフェイス メディエーター {
/**
* 同僚オブジェクトは、変更されるとメディエーター メソッドに通知します。
* メディエーターに他の同僚オブジェクトとの対応する対話を担当させます
*/
public void が変更されました(同僚 c);
}
特定のメディエーター クラスのコピー コードは次のとおりです。
public class MainBoard は Mediator を実装します {
//対話する同僚クラスを知る必要がある - 光学式ドライブ クラス
プライベート CDDriver cdDriver = null;
//CPU クラスと対話する同僚クラスを知る必要がある
プライベート CPU CPU = null;
//対話する同僚のクラス - グラフィックス カード クラスを知る必要がある
プライベート VideoCard ビデオカード = null;
//対話する同僚のクラス - サウンド カード クラスを知る必要がある
プライベート サウンドカード サウンドカード = null;
public void setCdDriver(CDDriver cdDriver) {
this.cdDriver = cdDriver;
}
public void setCpu(CPU cpu) {
this.cpu = CPU;
}
public void setVideoCard(VideoCard videoCard) {
this.videoCard = ビデオカード;
}
public void setSoundCard(SoundCard サウンドカード) {
this.soundCard = サウンドカード;
}
@オーバーライド
public void selected(Colleague c) {
if(c CDDriver のインスタンス){
//光学ドライブがデータを読み取ったことを示します
this.opeCDDriverReadData((CDDriver)c);
}else if(CPU のインスタンス){
this.opeCPU((CPU)c);
}
}
/**
* 光学式ドライブがデータを読み取った後、他のオブジェクトとの対話を処理します。
*/
private void opeCDDriverReadData(CDDriver cd){
//まず光学ドライブが読み取ったデータを取得します
文字列データ = cd.getData();
// これらのデータを CPU に転送して処理します
cpu.executeData(データ);
}
/**
* データ処理後に CPU と他のオブジェクト間の対話を処理します。
*/
private void opeCPU(CPU cpu){
// 最初に CPU によって処理されたデータを取得します
文字列ビデオデータ = cpu.getVideoData();
文字列 soundData = cpu.getSoundData();
//これらのデータをグラフィックス カードとサウンド カードに転送して表示します
videoCard.showData(videoData);
soundCard.soundData(soundData);
}
}
クライアント クラスのコピー コードは次のとおりです。
パブリック クラス クライアント {
public static void main(String[] args) {
//メディエーターの作成 - マザーボード
MainBoard メディエーター = new MainBoard();
//同僚クラスを作成する
CDDriver cd = 新しい CDDriver(メディエーター);
CPU cpu = 新しい CPU (メディエーター);
VideoCard vc = 新しい VideoCard(メディエーター);
SoundCard sc = 新しい SoundCard(メディエーター);
// メディエータに同僚全員を知らせます
mediator.setCdDriver(cd);
mediator.setCpu(cpu);
mediator.setVideoCard(vc);
mediator.setSoundCard(sc);
//映画の視聴を開始し、ディスクを光学ドライブに挿入すると、光学ドライブがディスクの読み取りを開始します。
cd.readCD();
}
}
実行結果は次のとおりです。
メディエーター パターンの利点
●ルーズカップリング
メディエーター パターンは、複数の同僚オブジェクト間の対話をメディエーター オブジェクトにカプセル化することで、同僚オブジェクトを疎結合し、基本的に相補的な依存関係を実現します。このようにして、以前のように「1 つの場所を移動して本体全体に影響を与える」のではなく、同僚オブジェクトを個別に変更して再利用することができます。
● インタラクションの一元管理
複数の同僚オブジェクトのインタラクションは、集中管理のためにメディエーター オブジェクトにカプセル化されているため、これらのインタラクティブな動作が変更された場合は、メディエーター オブジェクトを変更するだけで済みます。もちろん、すでに完成したシステムの場合は、メディエーター オブジェクトを拡張します。各同僚クラスを変更する必要はありません。
● 多対多が 1 対多になる
メディエーター パターンが使用されない場合、同僚オブジェクト間の関係は通常多対多になります。メディエーター オブジェクトの導入後、メディエーター オブジェクトと同僚オブジェクト間の関係は通常、双方向の 1 対多になります。これにより、オブジェクト間の関係が理解しやすくなり、実装が容易になります。
メディエーター パターンの欠点
メディエーター モデルの潜在的な欠点の 1 つは、過度の集中化です。同僚オブジェクトの相互作用が非常に大規模で複雑な場合、これらすべての複雑さがメディエーターに集中すると、メディエーター オブジェクトは非常に複雑になり、管理と保守が困難になります。