案例引入
案例一
普通實(shí)現(xiàn)
在租房過(guò)程中,客戶可能去找房東問(wèn)房子是否可以租,但是房東可能要和家人進(jìn)行一系列的溝通,最后還可能派出另一個(gè)家庭成員來(lái)和客戶進(jìn)行交流,整個(gè)溝通過(guò)程非常復(fù)雜、溝通線路繁多。如果是寫(xiě)成程序的模式,不同成員之間需要留好接口方便成員之間互相進(jìn)行調(diào)用
【分析】
- 各個(gè)成員彼此聯(lián)系,你中有我,我中有你,不利于松耦合
- 各個(gè)成員之間所傳遞的消息(參數(shù))容易混亂
- 當(dāng)系統(tǒng)增加一個(gè)新的成員時(shí),或者執(zhí)行流程改變時(shí),代碼的可維護(hù)性、擴(kuò)展性都不理想
【改進(jìn)】
- 使用中介者模式
中介者模式
客戶只需要對(duì)接中介,其他成員互相之間不進(jìn)行溝通,由中介來(lái)進(jìn)行溝通。如 屋主—>爸爸
變成 屋主—>中介—>爸爸
。通過(guò)中介的聯(lián)絡(luò),可以將成員之間的關(guān)聯(lián)關(guān)系都搞定
案例二
現(xiàn)在很多家庭都配備了智能家居,包括各種設(shè)備,如鬧鐘、咖啡機(jī)、電視機(jī)、窗簾……
當(dāng)主人想要看電視時(shí),會(huì)讓多個(gè)設(shè)備協(xié)同工作,來(lái)自動(dòng)完成看電視的準(zhǔn)備工作,比如流程為: 鬧鈴響起->咖啡機(jī)開(kāi)始做咖啡->窗簾自動(dòng)落下->電視機(jī)開(kāi)始播放
介紹
基礎(chǔ)介紹
- 在中介者模式中,團(tuán)隊(duì)組員之間不再互相溝通并私自做出決定,而是發(fā)生任何事情都向中介者報(bào)告,中介者站在整個(gè)團(tuán)隊(duì)的角度上對(duì)組員上報(bào)的事情做出決定。當(dāng)中介者下達(dá)指示時(shí),組員會(huì)立即執(zhí)行
- 用一個(gè)中介對(duì)象來(lái)封裝一系列的對(duì)象交互方法。中介者使各個(gè)對(duì)象不需要顯式地相互引用,從而使其耦合松散,而且可以獨(dú)立地改變它們之間的交互
- 中介者模式屬于行為型模式
- 在MVC模式中,C(Controller控制器) 是M(Model模型) 和V(View視圖)的中介者,在前后端交互時(shí)起到了中間人的作用
登場(chǎng)角色
尚硅谷
- Mediator 就是抽象中介者,定義了同事對(duì)象到中介者對(duì)象的接口
- Colleague 是抽象同事類
- ConcreteMediator 具體的中介者對(duì)象,實(shí)現(xiàn)抽象方法,他需要知道所有的具體同事類,即以HashMap管理所有同事類,并接受某個(gè)同事對(duì)象的消息,來(lái)協(xié)調(diào)其他同事完成相應(yīng)的任務(wù)
- ConcreteColleague 具體的同事類,可能會(huì)有很多個(gè),每個(gè)同事只知道自己的行為,而不了解其他同事類的行為(方法),他們都依賴中介者對(duì)象
《圖解設(shè)計(jì)模式》
-
Mediator(仲裁者、中介者)
:負(fù)責(zé)定義與 Colleague 角色進(jìn)行通信和做出決定的接口(API) -
ConcreteMediator(具體的仲裁者、中介者)
:負(fù)責(zé)實(shí)現(xiàn) Mediator 角色的接口(API),負(fù)責(zé)實(shí)際上如何做出決定 -
Colleague(同事)
:負(fù)責(zé)定義與Mediator角色進(jìn)行通信的接口(API) -
ConcreteColleague(具體的同事)
:負(fù)責(zé)實(shí)現(xiàn) Colleague 角色的接口(API)
案例實(shí)現(xiàn)
案例一:智能家庭
類圖
【操作流程】
- 創(chuàng)建 ConcreteMediator 對(duì)象
- 創(chuàng)建各個(gè)同事類對(duì)象,比如: Alarm、CoffeeMachine、TV
- 在創(chuàng)建同事類對(duì)象的時(shí)候,就直接通過(guò)構(gòu)造器,加入到 colleagueMap
- 同事類對(duì)象,可以調(diào)用sendMessage,最終會(huì)去調(diào)用ConcreteMediator的getMessage方法
- getMessage(核心方法)會(huì)根據(jù)接收到的同事對(duì)象發(fā)出的消息 來(lái)協(xié)調(diào)調(diào)用其它的同事對(duì)象來(lái)共同完成任務(wù)
實(shí)現(xiàn)
【抽象中介者類】
package com.atguigu.mediator.smarthouse;
public abstract class Mediator {
/**
* 將給中介者對(duì)象,加入到集合中
* @param colleagueName
* @param colleague
*/
public abstract void register(String colleagueName, Colleague colleague);
/**
* 接收消息, 具體的同事對(duì)象發(fā)出
* @param stateChange
* @param colleagueName
*/
public abstract void getMessage(int stateChange, String colleagueName);
public abstract void sendMessage();
}
【具體中介者類】
package com.atguigu.mediator.smarthouse;
import java.util.HashMap;
/**
* 具體的中介者類
*/
public class ConcreteMediator extends Mediator {
/**
* 集合,放入所有的同事對(duì)象
*/
private HashMap<String, Colleague> colleagueMap;
private HashMap<String, String> interMap;
public ConcreteMediator() {
colleagueMap = new HashMap<String, Colleague>();
interMap = new HashMap<String, String>();
}
@Override
public void register(String colleagueName, Colleague colleague) {
colleagueMap.put(colleagueName, colleague);
if (colleague instanceof Alarm) {
interMap.put("Alarm", colleagueName);
} else if (colleague instanceof CoffeeMachine) {
interMap.put("CoffeeMachine", colleagueName);
} else if (colleague instanceof TV) {
interMap.put("TV", colleagueName);
} else if (colleague instanceof Curtains) {
interMap.put("Curtains", colleagueName);
}
}
/**
* 具體中介者的核心方法
* 1. 根據(jù)得到消息,完成對(duì)應(yīng)任務(wù)
* 2. 中介者在這個(gè)方法,協(xié)調(diào)各個(gè)具體的同事對(duì)象,完成任務(wù)
* @param stateChange
* @param colleagueName
*/
@Override
public void getMessage(int stateChange, String colleagueName) {
//處理鬧鐘發(fā)出的消息
if (colleagueMap.get(colleagueName) instanceof Alarm) {
if (stateChange == 0) {
// 老司機(jī)做咖啡
((CoffeeMachine) (colleagueMap.get(interMap
.get("CoffeeMachine")))).StartCoffee();
// 啟動(dòng)電視
((TV) (colleagueMap.get(interMap.get("TV")))).StartTv();
} else if (stateChange == 1) {
// 關(guān)掉電視
((TV) (colleagueMap.get(interMap.get("TV")))).StopTv();
}
} else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) {
// 將窗簾升起來(lái)
((Curtains) (colleagueMap.get(interMap.get("Curtains"))))
.UpCurtains();
} else if (colleagueMap.get(colleagueName) instanceof TV) {
//如果TV發(fā)現(xiàn)消息
} else if (colleagueMap.get(colleagueName) instanceof Curtains) {
//如果是以窗簾發(fā)出的消息,這里處理...
}
}
@Override
public void sendMessage() {
}
}
【抽象同事類:Colleague】
package com.atguigu.mediator.smarthouse;
/**
* 抽象同事類
*/
public abstract class Colleague {
/**
* 關(guān)聯(lián) Mediator
*/
private Mediator mediator;
public String name;
public Colleague(Mediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public Mediator GetMediator() {
return this.mediator;
}
public abstract void SendMessage(int stateChange);
}
【具體同事類:鬧鐘】
package com.atguigu.mediator.smarthouse;
/**
* 具體的同事類 鬧鐘
*/
public class Alarm extends Colleague {
/**
* 構(gòu)造器
* @param mediator
* @param name
*/
public Alarm(Mediator mediator, String name) {
super(mediator, name);
//在創(chuàng)建Alarm 同事對(duì)象時(shí),將自己放入到ConcreteMediator 對(duì)象的集合中
mediator.register(name, this);
}
public void SendAlarm(int stateChange) {
SendMessage(stateChange);
}
@Override
public void SendMessage(int stateChange) {
//調(diào)用的中介者對(duì)象的getMessage
this.GetMediator().getMessage(stateChange, this.name);
}
}
【具體同事類:窗簾】
package com.atguigu.mediator.smarthouse;
/**
* 窗簾
*/
public class Curtains extends Colleague {
public Curtains(Mediator mediator, String name) {
super(mediator, name);
mediator.register(name, this);
}
@Override
public void SendMessage(int stateChange) {
this.GetMediator().getMessage(stateChange, this.name);
}
public void UpCurtains() {
System.out.println("I am holding Up Curtains!");
}
}
【具體同事類:電視】
package com.atguigu.mediator.smarthouse;
public class TV extends Colleague {
public TV(Mediator mediator, String name) {
super(mediator, name);
mediator.register(name, this);
}
@Override
public void SendMessage(int stateChange) {
this.GetMediator().getMessage(stateChange, this.name);
}
public void StartTv() {
System.out.println("It's time to StartTv!");
}
public void StopTv() {
System.out.println("StopTv!");
}
}
【主類】
package com.atguigu.mediator.smarthouse;
public class ClientTest {
public static void main(String[] args) {
//創(chuàng)建一個(gè)中介者對(duì)象
Mediator mediator = new ConcreteMediator();
//創(chuàng)建Alarm并且加入到 ConcreteMediator 對(duì)象的HashMap
Alarm alarm = new Alarm(mediator, "alarm");
//創(chuàng)建了CoffeeMachine對(duì)象,并且加入到 ConcreteMediator 對(duì)象的HashMap
CoffeeMachine coffeeMachine = new CoffeeMachine(mediator,
"coffeeMachine");
//創(chuàng)建 Curtains, 并且加入到 ConcreteMediator 對(duì)象的HashMap
Curtains curtains = new Curtains(mediator, "curtains");
TV tV = new TV(mediator, "TV");
//讓鬧鐘發(fā)出消息
alarm.SendAlarm(0);
//做好咖啡
coffeeMachine.FinishCoffee();
alarm.SendAlarm(1);
}
}
【運(yùn)行】
It's time to startcoffee!
It's time to StartTv!
After 5 minutes!
Coffee is ok!
I am holding Up Curtains!
StopTv!
Process finished with exit code 0
【分析】
- 程序拓展性較好:如果添加一個(gè)機(jī)器,只需要添加一個(gè) ConcreteColleague 并修改 ConcreteMediator 的相關(guān)方法就行,客戶端不用改變
案例二:登錄頁(yè)面邏輯實(shí)現(xiàn)
說(shuō)明
需要實(shí)現(xiàn)一個(gè)系統(tǒng)登錄表單功能,具體的處理邏輯如下:
- 如果選擇作為游客訪問(wèn),那么禁用用戶名輸入框和密碼輸入框,使用戶無(wú)法輸入
- 如果選擇作為用戶登錄,那么啟用用戶名輸入框和密碼輸入框,使用戶可以輸入
- 如果在用戶名輸入框中一個(gè)字符都沒(méi)有輸入,那么禁用密碼輸入框,使用戶無(wú)法輸入密碼
- 如果在用戶名輸入框中輸入了至少一個(gè)字符,那么啟用密碼輸入框,使用戶可以輸入密碼(當(dāng)然,如果選擇作為游客訪問(wèn),那么密碼框依然是禁用狀態(tài) )
- 只有當(dāng)用戶名輸入框和密碼輸入框中都至少輸入一個(gè)字符后,OK 按鈕才處于啟用狀態(tài),可以被按下
- 用戶名輸入框或密碼輸入框中一個(gè)字符都沒(méi)有被輸入的時(shí)候,禁用OK按鈕,使其不可被按下(當(dāng)然,如果選擇作為游客訪問(wèn),那么OK 按總是處于啟用狀態(tài))
- Cancel按鈕總是處于啟用狀態(tài),任何時(shí)候都可以按下該按鈕
類圖
實(shí)現(xiàn)
中介者接口和組員接口的方法并非固定就是這些,當(dāng)中介者和組員需要其他合作的話,就需要定義更多的方法
【中介者接口】
package com.atguigu.mediator.Sample;
public interface Mediator {
/**
* 生成 Mediator 管理的組員
*/
public abstract void createColleagues();
/**
* 每個(gè)組員都會(huì)調(diào)用這個(gè)方法來(lái)向中介者匯報(bào)
*/
public abstract void colleagueChanged();
}
【組員接口】
package com.atguigu.mediator.Sample;
public interface Colleague {
/**
* 設(shè)置中介者,告訴組員中介者是誰(shuí)
*
* @param mediator
*/
public abstract void setMediator(Mediator mediator);
/**
* 告知組員中介者所下達(dá)的指令
* @param enabled 控制是否啟用組員的功能
*/
public abstract void setColleagueEnabled(boolean enabled);
}
【組員:按鈕】
package com.atguigu.mediator.Sample;
import java.awt.*;
public class ColleagueButton extends Button implements Colleague {
private Mediator mediator;
public ColleagueButton(String caption) {
super(caption);
}
public void setMediator(Mediator mediator) {
// 保存Mediator
this.mediator = mediator;
}
public void setColleagueEnabled(boolean enabled) {
// Mediator下達(dá)啟用/禁用的指示
// setEnabled是java.awt.Button定義的方法,用來(lái)控制按鈕組件是啟用還是禁用,當(dāng)設(shè)置為false時(shí),按鈕無(wú)法被按下
setEnabled(enabled);
}
}
【組員:文本輸入框】
package com.atguigu.mediator.Sample;
import java.awt.*;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;
/**
* 實(shí)現(xiàn) TextListener 接口來(lái)實(shí)現(xiàn)監(jiān)聽(tīng)事件
*/
public class ColleagueTextField extends TextField implements TextListener, Colleague {
private Mediator mediator;
public ColleagueTextField(String text, int columns) { // 構(gòu)造函數(shù)
super(text, columns);
}
public void setMediator(Mediator mediator) { // 保存Mediator
this.mediator = mediator;
}
public void setColleagueEnabled(boolean enabled) { // Mediator下達(dá)啟用/禁用的指示
setEnabled(enabled);
// 控件啟用時(shí),背景色變成白色;否則變?yōu)榛疑?/span>
setBackground(enabled ? Color.white : Color.lightGray);
}
/**
* TextListener 中定義的方法,監(jiān)聽(tīng)文本內(nèi)容的變化,并通知中介者
* @param e
*/
public void textValueChanged(TextEvent e) {
// 當(dāng)文字發(fā)生變化時(shí)通知Mediator
mediator.colleagueChanged();
}
}
【組員:?jiǎn)芜x按鈕】
package com.atguigu.mediator.Sample;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
/**
* 單選按鈕
*/
public class ColleagueCheckbox extends Checkbox implements ItemListener, Colleague {
private Mediator mediator;
public ColleagueCheckbox(String caption, CheckboxGroup group, boolean state) {
// 構(gòu)造函數(shù)
super(caption, group, state);
}
public void setMediator(Mediator mediator) {
// 保存Mediator
this.mediator = mediator;
}
public void setColleagueEnabled(boolean enabled) {
// Mediator下達(dá)啟用/禁用指示
setEnabled(enabled);
}
/**
* 監(jiān)聽(tīng)單選按鈕的狀態(tài)變化
* @param e
*/
public void itemStateChanged(ItemEvent e) {
// 當(dāng)狀態(tài)發(fā)生變化時(shí)通知Mediator
mediator.colleagueChanged();
}
}
【具體中介者】
package com.atguigu.mediator.Sample;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* 具體中介者
*/
public class LoginFrame extends Frame implements ActionListener, Mediator {
private ColleagueCheckbox checkGuest;
private ColleagueCheckbox checkLogin;
private ColleagueTextField textUser;
private ColleagueTextField textPass;
private ColleagueButton buttonOk;
private ColleagueButton buttonCancel;
/**
* 構(gòu)造函數(shù)
* 生成并配置各個(gè)Colleague后,顯示對(duì)話框
*
* @param title
*/
public LoginFrame(String title) {
super(title);
// 設(shè)置登錄對(duì)話框的背景顏色
setBackground(Color.lightGray);
// 使用布局管理器生成4×2窗格
setLayout(new GridLayout(4, 2));
// 生成各個(gè) Colleague
createColleagues();
// 配置 Colleague
add(checkGuest);
add(checkLogin);
add(new Label("Username:"));
add(textUser);
add(new Label("Password:"));
add(textPass);
add(buttonOk);
add(buttonCancel);
// 設(shè)置初始的啟用起用/禁用狀態(tài)
colleagueChanged();
// 顯示
pack();
show();
}
/**
* 生成登錄對(duì)話框所需要的各個(gè)Colleague
*/
public void createColleagues() {
// 生成
CheckboxGroup g = new CheckboxGroup();
checkGuest = new ColleagueCheckbox("Guest", g, true);
checkLogin = new ColleagueCheckbox("Login", g, false);
textUser = new ColleagueTextField("", 10);
textPass = new ColleagueTextField("", 10);
textPass.setEchoChar('*');
buttonOk = new ColleagueButton("OK");
buttonCancel = new ColleagueButton("Cancel");
// 設(shè)置Mediator
checkGuest.setMediator(this);
checkLogin.setMediator(this);
textUser.setMediator(this);
textPass.setMediator(this);
buttonOk.setMediator(this);
buttonCancel.setMediator(this);
// 設(shè)置Listener
checkGuest.addItemListener(checkGuest);
checkLogin.addItemListener(checkLogin);
textUser.addTextListener(textUser);
textPass.addTextListener(textPass);
buttonOk.addActionListener(this);
buttonCancel.addActionListener(this);
}
/**
* 控制各個(gè)成員的狀態(tài)
* 接收來(lái)自于 Colleague 的通知,然后判斷各 Colleague 的啟用/禁用狀態(tài)
*
* 單選按鈕的選中狀態(tài)發(fā)生改變 或者 文本輸入框的內(nèi)容發(fā)生改變,都會(huì)調(diào)用這個(gè)方法
*/
public void colleagueChanged() {
// checkGuest.getState()獲取游客模式的按鈕是否處于選中狀態(tài)
if (checkGuest.getState()) {
// 游客模式
textUser.setColleagueEnabled(false);
textPass.setColleagueEnabled(false);
buttonOk.setColleagueEnabled(true);
} else {
// 登錄模式
textUser.setColleagueEnabled(true);
userpassChanged();
}
}
/**
* 當(dāng)textUser或是textPass文本輸入框中的文字發(fā)生變化時(shí)
* 判斷各Colleage的啟用/禁用狀態(tài)
*/
private void userpassChanged() {
if (textUser.getText().length() > 0) {
textPass.setColleagueEnabled(true);
if (textPass.getText().length() > 0) {
buttonOk.setColleagueEnabled(true);
} else {
buttonOk.setColleagueEnabled(false);
}
} else {
textPass.setColleagueEnabled(false);
buttonOk.setColleagueEnabled(false);
}
}
public void actionPerformed(ActionEvent e) {
System.out.println(e.toString());
System.exit(0);
}
}
【主類】
package com.atguigu.mediator.Sample;
public class Main {
static public void main(String args[]) {
new LoginFrame("Mediator Sample");
}
}
【運(yùn)行】
總結(jié)
【優(yōu)點(diǎn)】文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-634464.html
- 多個(gè)類相互耦合,會(huì)形成網(wǎng)狀結(jié)構(gòu)(通信路線很多),使用中介者模式將網(wǎng)狀結(jié)構(gòu)分離為星型結(jié)構(gòu)進(jìn)行解耦
- 減少類間依賴,峰低了耦合,符合迪米特原則
- 如果出現(xiàn)了Bug,比較容易定位Bug的位置
- ConcreteColleague容易復(fù)用(如果需要寫(xiě)一個(gè)新的對(duì)話框,那么按鈕、文本輸入框都可以很容易使用到新的對(duì)話框中)
【缺點(diǎn)】文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-634464.html
- 中介者承擔(dān)了較多的責(zé)任,一旦中介者出現(xiàn)了問(wèn)題,整個(gè)系統(tǒng)就會(huì)受到影響
- 如果設(shè)計(jì)不當(dāng),中介者對(duì)象本身變得過(guò)于復(fù)雜,這點(diǎn)在實(shí)際使用時(shí),要特別注意
- ConcreteMediator難以復(fù)用,因?yàn)槠湟蕾囉谔囟ǖ膽?yīng)用程序
文章說(shuō)明
- 本文章為本人學(xué)習(xí)尚硅谷的學(xué)習(xí)筆記,文章中大部分內(nèi)容來(lái)源于尚硅谷視頻(點(diǎn)擊學(xué)習(xí)尚硅谷相關(guān)課程),也有部分內(nèi)容來(lái)自于自己的思考,發(fā)布文章是想幫助其他學(xué)習(xí)的人更方便地整理自己的筆記或者直接通過(guò)文章學(xué)習(xí)相關(guān)知識(shí),如有侵權(quán)請(qǐng)聯(lián)系刪除,最后對(duì)尚硅谷的優(yōu)質(zhì)課程表示感謝。
- 本人還同步閱讀《圖解設(shè)計(jì)模式》書(shū)籍(圖解設(shè)計(jì)模式/(日)結(jié)城浩著;楊文軒譯–北京:人民郵電出版社,2017.1),進(jìn)而綜合兩者的內(nèi)容,讓知識(shí)點(diǎn)更加全面
到了這里,關(guān)于【設(shè)計(jì)模式——學(xué)習(xí)筆記】23種設(shè)計(jì)模式——中介者模式Observer(原理講解+應(yīng)用場(chǎng)景介紹+案例介紹+Java代碼實(shí)現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!