個(gè)人學(xué)習(xí)筆記分享,當(dāng)前能力有限,請(qǐng)勿貶低,菜鳥互學(xué),大佬繞道
如有勘誤,歡迎指出和討論,本文后期也會(huì)進(jìn)行修正和補(bǔ)充
前言
相信很多人都對(duì)更換手機(jī)號(hào)有所顧慮,或者體驗(yàn)過了這種麻煩——通知每個(gè)人自己手機(jī)號(hào)變更了,親友朋友同事上司全都得通知一遍,萬一漏了,就失聯(lián)了,甚至還得確保他們收到且更換了通訊錄中你的號(hào)碼
那么,我們能不能將手機(jī)號(hào)存在一個(gè)中介處,我們只需要告知中介處我們的新手機(jī)號(hào)。當(dāng)有人想找我們的時(shí)候,他們也只需要詢問中介就行了。
比如公司、班級(jí)、家庭的通訊錄,自己手機(jī)號(hào)或者地址修改后,也同步修改一下就好了,找人的時(shí)候去上面查找即可。
同樣的例子,還有人才交流市場(chǎng)、聊天室、房屋中介等等,甚至是前臺(tái)小姐姐都算是中介者~
那么我們的網(wǎng)狀關(guān)系結(jié)構(gòu),就變成了星型關(guān)系結(jié)構(gòu)
簡(jiǎn)而言之,其目的就是為了==降低對(duì)象之間的耦合性,從而提高其靈活性==
1.介紹
適用目的:定義一個(gè)中介對(duì)象來封裝一系列對(duì)象之間的交互,使原有對(duì)象之間的耦合松散,且可以獨(dú)立地改變它們之間的交互。
主要解決:對(duì)象之間存在大量的關(guān)聯(lián)關(guān)系,會(huì)導(dǎo)致系統(tǒng)結(jié)構(gòu)復(fù)雜,若某一個(gè)對(duì)象發(fā)生改變,則需要跟蹤處理所有有關(guān)的對(duì)象。
何時(shí)使用:多個(gè)指責(zé)相似的類相互耦合,形成了網(wǎng)狀結(jié)構(gòu)。
如何解決:對(duì)象 Colleague 之間的通信封裝到一個(gè)類中統(tǒng)一處理。
關(guān)鍵代碼:定義中介者,用于存儲(chǔ)和管理Colleague;定義Colleague持有中介者,并通過中介者相互通訊;
應(yīng)用實(shí)例:聊天室;MVC框架;資源調(diào)度系統(tǒng);
優(yōu)點(diǎn):
- 降低了類的復(fù)雜度,將一對(duì)多轉(zhuǎn)化成了一對(duì)一,提高了靈活性,易于擴(kuò)展和維護(hù)
- 各個(gè)類之間的解耦,降低了系統(tǒng)的耦合度
- 類之間各司其職,符合迪米特原則
缺點(diǎn):Colleague越多,則中介者越臃腫,越難以維護(hù)
使用場(chǎng)景:
- 系統(tǒng)中對(duì)象之間存在比較復(fù)雜的引用關(guān)系,導(dǎo)致它們之間的依賴關(guān)系結(jié)構(gòu)混亂而且難以復(fù)用該對(duì)象
- 想通過一個(gè)中間類來封裝多個(gè)類中的行為,而又不想生成太多的子類。
- 大量相似的類,相互之間若是網(wǎng)狀關(guān)系則十分混亂
注意事項(xiàng):不應(yīng)當(dāng)在職責(zé)混亂的時(shí)候使用。
2.結(jié)構(gòu)
中介者模式包含以下主要角色:
- 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事對(duì)象注冊(cè)與轉(zhuǎn)發(fā)同事對(duì)象信息的抽象方法。
- 具體中介者(Concrete Mediator)角色:實(shí)現(xiàn)中介者接口,定義一個(gè) List 來管理同事對(duì)象,協(xié)調(diào)各個(gè)同事角色之間的交互關(guān)系,因此它依賴于同事角色。
- 抽象同事類(Colleague)角色:定義同事類的接口,保存中介者對(duì)象,提供同事對(duì)象交互的抽象方法,實(shí)現(xiàn)所有相互影響的同事類的公共功能。
- 具體同事類(Concrete Colleague)角色:是抽象同事類的實(shí)現(xiàn)者,當(dāng)需要與其他同事對(duì)象交互時(shí),由中介者對(duì)象負(fù)責(zé)后續(xù)的交互。

3.實(shí)現(xiàn)步驟
3.1.簡(jiǎn)單實(shí)例:模擬聊天室
業(yè)務(wù)流程如下:
- 用戶進(jìn)入聊天室時(shí)進(jìn)行注冊(cè)
- 用戶發(fā)送消息時(shí),會(huì)將消息轉(zhuǎn)發(fā)給聊天室的其他用戶
- 用戶收到消息時(shí),顯示消息內(nèi)容和發(fā)送者
代碼如下:
-
定義抽象中介者
//抽象中介者 abstract class Mediator { public abstract void register(Colleague colleague); public abstract void relay(Colleague colleague,String msg); } -
定義具體中介者,實(shí)現(xiàn)抽象中介者的功能
class ConcreteMediator extends Mediator { private Set<Colleague> colleagues = new HashSet<>(); @Override public void register(Colleague colleague) { if (!colleagues.contains(colleague)) { colleagues.add(colleague); } } @Override public void relay(Colleague colleague,String msg) { for (Colleague item : colleagues) { if (!Objects.equals(item, colleague)) { item.receive(colleague,msg); } } } }此處使用
Set作為容器,也可以使用List、Map、Queue等容器,沒啥區(qū)別轉(zhuǎn)發(fā)的行為也可以是存入消息隊(duì)列、命令池等,視情況而定
請(qǐng)注意此處用對(duì)象比較,不要使用其內(nèi)部的參數(shù),避免兩個(gè)不同的人使用相同標(biāo)識(shí)符的情況,也給同事類留出更大的自定義擴(kuò)展空間
-
定義抽象同事類
abstract class Colleague { protected Mediator mediator; public String name; public Colleague(Mediator mediator, String name) { this.mediator = mediator; this.name = name; } public abstract void receive(Colleague colleague, String msg); public abstract void send(String msg); }此處的mediator即中介者,被同事持有,以便于向中介者傳遞消息,當(dāng)然也可以使用單例模式或者向接口發(fā)送消息等解決方案
此處的name為同事類的標(biāo)識(shí)符,即用于分辨同事是誰,也可以使用uid等作為標(biāo)識(shí)符
請(qǐng)注意此處的標(biāo)識(shí)符并不唯一,相當(dāng)于生活中的姓名,是可能也允許出現(xiàn)重復(fù)的
-
定義具體同事類,實(shí)現(xiàn)抽象同事類
class ConcreteColleague extends Colleague { public ConcreteColleague(Mediator mediator, String name) { super(mediator, name); } @Override public void receive(Colleague colleague,String msg) { System.out.println(name + " receive message from "+colleague.name+": " + msg); } @Override public void send(String msg) { System.out.println(name + " send message: " + msg); mediator.relay(this,msg); } }具體同事類可以定義多個(gè),每個(gè)可以自行定義收發(fā)消息的行為,也可以存儲(chǔ)自定義的信息
例如:
定義老師類,存儲(chǔ)老師的聯(lián)系方式、教師編號(hào)等信息
定義學(xué)生類,存儲(chǔ)學(xué)生的學(xué)號(hào)、所在班級(jí)等信息
定義管理員類,存儲(chǔ)聯(lián)系方式即可
但存在多種同事類的情況下,建議在抽象同事類中留有類型標(biāo)記(如
int type),以便于中介者識(shí)別
測(cè)試代碼
public class Test {
public static void main(String[] args) {
Mediator mediator=new ConcreteMediator();
Colleague colleagueA=new ConcreteColleague(mediator,"A");
Colleague colleagueB=new ConcreteColleague(mediator,"B");
Colleague colleagueC=new ConcreteColleague(mediator,"C");
mediator.register(colleagueA);
mediator.register(colleagueB);
mediator.register(colleagueC);
colleagueA.send("Hello! I'm A");
System.out.println("-------------");
colleagueB.send("Hello! I'm B");
System.out.println("-------------");
colleagueC.send("Hello! I'm C");
}
}
運(yùn)行結(jié)果

3.2.進(jìn)階實(shí)例:模擬聊天室
模擬業(yè)務(wù):
- 建立一個(gè)聊天室,有三種類型的人員:學(xué)生、老師、GM
- 每種身份均允許多個(gè)人參與
- 每個(gè)人擁有一個(gè)獨(dú)立的聊天框,顯示所有人發(fā)送的消息
全部代碼:
package com.company.designPattern.mediator.test2;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
/**
* @Description: 聊天室demo代碼
* @Author: Echo
* @Time: 2021/2/25 10:22
* @Email: 347110596@qq.com
*/
public class Test {
public static void main(String[] args) {
//初始化聊天室
Medium md = new ChatRoom();
//初始化參與者
Colleague member1 = new Student("小明");
Colleague member2 = new Student("小紅");
Colleague member3 = new Teacher("張老師");
Colleague member4 = new Manager("GM");
//注冊(cè)
md.register(member1);
md.register(member2);
md.register(member3);
md.register(member4);
}
}
enum Types {
Teacher("老師"),
Student("學(xué)生"),
Manager("管理員"),
;
private final String TypeName;
public String getTypeName() {
return TypeName;
}
Types(String typeName) {
TypeName = typeName;
}
}
//抽象中介者
interface Medium {
void register(Colleague member); //注冊(cè)
void relay(Colleague from, String ad); //轉(zhuǎn)發(fā)
}
//具體中介者:房地產(chǎn)中介
class ChatRoom implements Medium {
private List<Colleague> members = new ArrayList<>();
public void register(Colleague member) {
if (!members.contains(member)) {
members.add(member);
member.setMedium(this);
}
}
public void relay(Colleague from, String msg) {
for (Colleague to : members) {
if (!Objects.equals(from, to)) {
to.receive(from, msg);
}
}
}
}
//抽象同事類
abstract class Colleague extends JFrame implements ActionListener {
private static final long serialVersionUID = -7219939540794786080L;
protected Medium medium;
protected String name;
protected Types identify;
JTextField SentText;
JTextArea ReceiveArea;
public Colleague(String name, Types identify) {
//初始化彈窗標(biāo)題
super(name);
//初始化同事類信息
this.name = name;
this.identify = identify;
//初始化彈窗
ClientWindow();
}
//定義客戶端
void ClientWindow() {
//初始化容器
Container cp = this.getContentPane();
ReceiveArea = new JTextArea(10, 18);
ReceiveArea.setEditable(false);
SentText = new JTextField(18);
// 接收消息模塊
JPanel p1 = new JPanel();
p1.setBorder(BorderFactory.createTitledBorder("接收內(nèi)容:"));
p1.add(ReceiveArea);
// 初始化消息內(nèi)容
JScrollPane sp = new JScrollPane(p1);
// 置頂
cp.add(sp, BorderLayout.NORTH);
// 發(fā)送消息模塊
JPanel p2 = new JPanel();
p2.setBorder(BorderFactory.createTitledBorder("發(fā)送內(nèi)容:"));
p2.add(SentText);
// 置底
cp.add(p2, BorderLayout.SOUTH);
// 輸入框設(shè)置監(jiān)聽
SentText.addActionListener(this);
// 設(shè)置客戶端窗口屬性
this.setLocation(50, 100); // 窗口位置
this.setSize(250, 330);// 窗口大小
this.setResizable(false); // 窗口大小不可調(diào)整
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 窗口關(guān)閉操作-退出
this.setVisible(true); // 窗口可見
}
@Override
public void actionPerformed(ActionEvent e) {
// 發(fā)送消息
String tempInfo = SentText.getText().trim();
this.send(tempInfo);
// 輸入框重置為空
SentText.setText("");
}
@Override
public String getName() {
return name;
}
public void setMedium(Medium medium) {
this.medium = medium;
}
public abstract void send(String ad);
public abstract void receive(Colleague from, String ad);
}
//具體同事類
class CommonColleague extends Colleague {
private static final long serialVersionUID = -1443076716629516027L;
public CommonColleague(String name, Types identify) {
super(name, identify);
}
public void send(String msg) {
// 顯示消息
ReceiveArea.append("我: " + msg + "\n");
// 使?jié)L動(dòng)條滾動(dòng)到最底端
ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
// 轉(zhuǎn)發(fā)消息
medium.relay(this, msg);
}
public void receive(Colleague from, String msg) {
// 獲取發(fā)送者身份
String sender = from.name + "(" + from.identify.getTypeName() + ")";
// 顯示消息
ReceiveArea.append(sender + ": " + msg + "\n");
// 使?jié)L動(dòng)條滾動(dòng)到最底端
ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
}
}
//具體同事類:學(xué)生
class Student extends CommonColleague {
private String stuNum;// 學(xué)號(hào)
private String classId;// 班級(jí)編號(hào)
//身份
private final static Types identify = Types.Student;
public Student(String name) {
super(name, identify);
}
}
//具體同事類:老師
class Teacher extends CommonColleague {
private String phone;// 手機(jī)號(hào)
//身份
private final static Types identify = Types.Teacher;
public Teacher(String name) {
super(name, identify);
}
}
//具體同事類:管理員
class Manager extends CommonColleague {
//身份
private final static Types identify = Types.Manager;
public Manager(String name) {
super(name, identify);
}
}
運(yùn)行結(jié)果:

相關(guān)demo地址:https://gitee.com/echo_ye/practice/tree/master/src/main/java/com/company/designPattern/mediator
4.實(shí)際使用
在實(shí)際開發(fā)中,通常并不嚴(yán)格按照上述模式開發(fā),而通過簡(jiǎn)化來使快速開發(fā),并縮小項(xiàng)目體積
不定義中介者接口,直接使用中介者(不太建議):效果顯而易見,但即便只有一個(gè)中介者也不太建議,使項(xiàng)目更簡(jiǎn)單很重要,但易于擴(kuò)展同樣重要
同事類不持有中介者,使用時(shí)直接獲取并調(diào)用(建議):相互持有是一個(gè)很有風(fēng)險(xiǎn)的事情,避開風(fēng)險(xiǎn)顯然有利無害
單例化中介者(建議):若只有一個(gè)中介者,將其單例化可以避免誤操作再次實(shí)例化;當(dāng)然多個(gè)中介者的話就不能這樣了
后記
中介者模式的核心目的是將網(wǎng)狀結(jié)構(gòu)轉(zhuǎn)換為星型結(jié)構(gòu),即從N-N改變?yōu)镹-1-N,從而降低系統(tǒng)的復(fù)雜度
作者:Echo_Ye
WX:Echo_YeZ
Email :echo_yezi@qq.com
個(gè)人站點(diǎn):在搭了在搭了。。。(右鍵 - 新建文件夾)