中介者模式的定義
中介者模式的定義為:Define an object that encapsulates how a set of objectsinteract.Mediator promotes loose coupling by keeping objects from referring to each otherexplicitly,and it lets you vary their interaction independently.(用一個中介對象封裝一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使其耦合松散,而且可以獨(dú)立地改變它們之間的交互。)
中介者模式通用類圖

從類圖中看,中介者模式由以下幾部分組成:
- Mediator 抽象中介者角色
抽象中介者角色定義統(tǒng)一的接口,用于各同事角色之間的通信。 - Concrete Mediator 具體中介者角色
具體中介者角色通過協(xié)調(diào)各同事角色實(shí)現(xiàn)協(xié)作行為,因此它必須依賴于各個同事角色。 - Colleague 同事角色
每一個同事角色都知道中介者角色,而且與其他的同事角色通信的時候,一定要通過中介者角色協(xié)作。每個同事類的行為分為兩種:一種是同事本身的行為,比如改變對象本身的狀態(tài),處理自己的行為等,這種行為叫做自發(fā)行為(Self-Method),與其他的同事類或中介者沒有任何的依賴;第二種是必須依賴中介者才能完成的行為,叫做依賴方法(Dep-Method)。
演示代碼
今天我們的任務(wù)是設(shè)計進(jìn)銷存系統(tǒng),聽起來很像是數(shù)據(jù)庫設(shè)計,不過這次是設(shè)計模式??磮D:

我們從這個示意圖上可以看出,三個模塊是相互依賴的。我們就以一個終端銷售商(以服務(wù)最終客戶為目標(biāo)的企業(yè),比如某某超市、某某商店等)為例,采購部門要采購IBM的電腦,它根據(jù)以下兩個要素來決定采購數(shù)量。
- 銷售情況
銷售部門要反饋銷售情況,暢銷就多采購,滯銷就不采購。 - 庫存情況
即使是暢銷產(chǎn)品,庫存都有1000臺了,每天才賣出去10臺,也就不需要再采購了!
銷售模塊是企業(yè)的贏利核心,對其他兩個模塊也有影響: - 庫存情況
庫房有貨,才能銷售,空手套白狼是不行的。 - 督促采購
經(jīng)過需求分析,我們的攻城獅就設(shè)計出了類圖。

開始工作吧??!
1.定義庫存,這是進(jìn)銷存的核心。
public class Stock {
// 剛開始有100臺電腦
private static int COMPUTER_NUMBER = 100;
// 庫存增加
public void increase(int number) {
COMPUTER_NUMBER = COMPUTER_NUMBER + number;
System.out.println("庫存數(shù)量為:" + COMPUTER_NUMBER);
}
// 庫存降低
public void decrease(int number) {
COMPUTER_NUMBER = COMPUTER_NUMBER - number;
System.out.println("庫存數(shù)量為:" + COMPUTER_NUMBER);
}
// 獲得庫存數(shù)量
public int getStockNumber() {
return COMPUTER_NUMBER;
}
// 存貨壓力大了,就要通知采購人員不要采購,銷售人員要盡快銷售
public void clearStock() {
Purchase purchase = new Purchase();
Sale sale = new Sale();
System.out.println("清理存貨數(shù)量為:" + COMPUTER_NUMBER);
// 要求折價銷售
sale.offSale();
// 要求采購人員不要采購
purchase.refuseBuyIBM();
}
}
庫房中的貨物數(shù)量肯定有增減,同時庫房還有一個容量顯示,達(dá)到一定的容量后就要求對一些商品進(jìn)行折價處理,以騰出更多的空間容納新產(chǎn)品。于是就有了clearStock方法,既然是清倉處理肯定就要折價銷售了。
2.庫存不足要及時補(bǔ)充,所以不能沒有采購人員
public class Purchase {
//采購IBM型號的電腦
public void buyIBMcomputer(int number){
//訪問庫存
Stock stock = new Stock();
//訪問銷售
Sale sale = new Sale();
//電腦的銷售情況
int saleStatus = sale.getSaleStatus();
if(saleStatus>80){ //銷售情況良好
System.out.println("采購IBM電腦:"+number + "臺");
stock.increase(number);
}else{ //銷售情況不好
int buyNumber = number/2; //折半采購
System.out.println("采購IBM電腦:"+buyNumber+ "臺");
}
}
//不再采購IBM電腦
public void refuseBuyIBM(){
System.out.println("不再采購IBM電腦");
}
}
Purchase定義了采購電腦的標(biāo)準(zhǔn):如果銷售情況比較好,大于80分,你讓我采購多少我就采購多少;銷售情況不好,你讓我采購100臺,我就采購50臺,對折采購。電腦采購?fù)戤?,需要放到庫房中,因此要調(diào)用庫存的方法,增加庫存電腦數(shù)量。
3.銷售才有利潤,才能給員工發(fā)工資。
public class Sale {
// 銷售IBM型號的電腦
public void sellIBMComputer(int number) { // 訪問庫存
Stock stock = new Stock(); // 訪問采購
Purchase purchase = new Purchase();
if (stock.getStockNumber() < number) { // 庫存數(shù)量不夠銷售
purchase.buyIBMcomputer(number);
}
System.out.println("銷售IBM電腦" + number + "臺");
stock.decrease(number);
}
// 反饋銷售情況,0——100之間變化,0代表根本就沒人賣,100代表非常暢銷,出1一個賣一個
public int getSaleStatus() {
Random rand = new Random(System.currentTimeMillis());
int saleStatus = rand.nextInt(100);
System.out.println("IBM電腦的銷售情況為:" + saleStatus);
return saleStatus;
}
// 折價處理
public void offSale() { // 庫房有多少賣多少
Stock stock = new Stock();
System.out.println("折價銷售IBM電腦" + stock.getStockNumber() + "臺");
}
}
測試一波吧??!
public class Client {
public static void main(String[] args) {
//采購人員采購電腦
System.out.println("------采購人員采購電腦--------");
Purchase purchase = new Purchase();
purchase.buyIBMcomputer(100);
//銷售人員銷售電腦
System.out.println("\n------銷售人員銷售電腦--------");
Sale sale = new Sale();
sale.sellIBMComputer(1);
//庫房管理人員管理庫存
System.out.println("\n------庫房管理人員清庫處理--------");
Stock stock = new Stock();
stock.clearStock();
}
}
------采購人員采購電腦--------
IBM電腦的銷售情況為:96
采購IBM電腦:100臺
庫存數(shù)量為:200------銷售人員銷售電腦--------
銷售IBM電腦1臺
庫存數(shù)量為:199------庫房管理人員清庫處理--------
清理存貨數(shù)量為:199
折價銷售IBM電腦199臺
不再采購IBM電腦
運(yùn)行結(jié)果也是我們期望的,三個不同類型的參與者完成了各自的活動。你有沒有發(fā)現(xiàn)這三個類是彼此關(guān)聯(lián)的?每個類都與其他兩個類產(chǎn)生了關(guān)聯(lián)關(guān)系。迪米特法則認(rèn)為“每個類只和朋友類交流”,這個朋友類并非越多越好,朋友類越多,耦合性越大,要想修改一個就得修改一片,這不是面向?qū)ο笤O(shè)計所期望的。
那么我們重新改進(jìn)一下吧!從設(shè)計圖開始改進(jìn)。

加入了一個中介者作為三個模塊的交流核心,每個模塊之間不再相互交流,要交流就通過中介者進(jìn)行。每個模塊只負(fù)責(zé)自己的業(yè)務(wù)邏輯,不屬于自己的則丟給中介者來處理,簡化了各模塊之間的耦合關(guān)系。類圖也要變。

重新開始工作吧!!!
1.抽象中介者,這是一切的核心。
public abstract class AbstractMediator {
protected Purchase purchase;
protected Sale sale;
protected Stock stock;
//構(gòu)造函數(shù)
public AbstractMediator(){
purchase = new Purchase(this);
sale = new Sale(this);
stock = new Stock(this);
}
//中介者最重要的方法,叫做事件方法,處理多個對象之間的關(guān)系
public abstract void execute(String str,Object...objects);
}
再來看具體的中介者,我們可以根據(jù)業(yè)務(wù)的要求產(chǎn)生多個中介者,并劃分各中介者的職責(zé)。
2.具體中介者
public class Mediator extends AbstractMediator {
//中介者最重要的方法
public void execute(String str,Object...objects){
if(str.equals("purchase.buy")){//采購電腦
this.buyComputer((Integer)objects[0]);
}else if(str.equals("sale.sell")){ //銷售電腦
this.sellComputer((Integer)objects[0]);
}else if(str.equals("sale.offsell")){ //折價銷售
this.offSell();
}else if(str.equals("stock.clear")){ //清倉處理
this.clearStock();
}
}
//采購電腦
private void buyComputer(int number){
int saleStatus = super.sale.getSaleStatus();
if(saleStatus>80){ //銷售情況良好
System.out.println("采購IBM電腦:"+number + "臺");
super.stock.increase(number);
}else{ //銷售情況不好
int buyNumber = number/2; //折半采購
System.out.println("采購IBM電腦:"+buyNumber+ "臺");
}
}
//銷售電腦
private void sellComputer(int number){
if(super.stock.getStockNumber()<number){ //庫存數(shù)量不夠銷售
super.purchase.buyIBMcomputer(number);
}
super.stock.decrease(number);
}
//折價銷售電腦
private void offSell(){
System.out.println("折價銷售IBM電腦"+stock.getStockNumber()+"臺");
}
//清倉處理
private void clearStock(){
//要求清倉銷售
super.sale.offSale();
//要求采購人員不要采購
super.purchase.refuseBuyIBM();
}
}
中介者M(jìn)ediator定義了多個private方法,其目的是處理各個對象之間的依賴關(guān)系,就是說把原有一個對象要依賴多個對象的情況移到中介者的private方法中實(shí)現(xiàn)。在實(shí)際項(xiàng)目中,一般的做法是中介者按照職責(zé)進(jìn)行劃分,每個中介者處理一個或多個類似的關(guān)聯(lián)請求。由于要使用中介者,我們增加了一個抽象同事類,三個具體的實(shí)現(xiàn)類分別繼承該抽象類。
3.抽象同事類
public abstract class AbstractColleague {
protected AbstractMediator mediator;
public AbstractColleague(AbstractMediator _mediator){
this.mediator = _mediator;
}
}
4.分別實(shí)現(xiàn)3個同事類。
采購
public class Purchase extends AbstractColleague{
public Purchase(AbstractMediator _mediator){
super(_mediator);
}
//采購IBM型號的電腦
public void buyIBMcomputer(int number){
super.mediator.execute("purchase.buy", number);
}
//不在采購IBM電腦 public void refuseBuyIBM(){
System.out.println("不再采購IBM電腦");
}
}
庫存
public class Stock extends AbstractColleague {
public Stock(AbstractMediator _mediator){
super(_mediator);
} //剛開始有100臺電腦
private static int COMPUTER_NUMBER =100; //庫存增加
public void increase(int number){
COMPUTER_NUMBER = COMPUTER_NUMBER + number;
System.out.println("庫存數(shù)量為:"+COMPUTER_NUMBER);
}
//庫存降低
public void decrease(int number){
COMPUTER_NUMBER = COMPUTER_NUMBER - number;
System.out.println("庫存數(shù)量為:"+COMPUTER_NUMBER);
}
//獲得庫存數(shù)量
public int getStockNumber(){
return COMPUTER_NUMBER;
}
//存貨壓力大了,就要通知采購人員不要采購,銷售人員要盡快銷售
public void clearStock(){
System.out.println("清理存貨數(shù)量為:"+COMPUTER_NUMBER);
super.mediator.execute("stock.clear");
}
}
銷售
public class Sale extends AbstractColleague {
public Sale(AbstractMediator _mediator){
super(_mediator);
}
//銷售IBM型號的電腦
public void sellIBMComputer(int number){
super.mediator.execute("sale.sell", number);
System.out.println("銷售IBM電腦"+number+"臺");
}
//反饋銷售情況,0——100之間變化,0代表根本就沒人賣,100代表非常暢銷,出1一個賣一個
public int getSaleStatus(){
Random rand = new Random(System.currentTimeMillis());
int saleStatus = rand.nextInt(100);
System.out.println("IBM電腦的銷售情況為:"+saleStatus);
return saleStatus;
}
//折價處理
public void offSale(){
super.mediator.execute("sale.offsell");
}
}
好了終于結(jié)束了,老大要驗(yàn)收了。
public class Client {
public static void main(String[] args) {
AbstractMediator mediator = new Mediator();
//采購人員采購電腦
System.out.println("------采購人員采購電腦--------");
Purchase purchase = new Purchase(mediator);
purchase.buyIBMcomputer(100); //銷售人員銷售電腦
System.out.println("\n------銷售人員銷售電腦--------");
Sale sale = new Sale(mediator);
sale.sellIBMComputer(1); //庫房管理人員管理庫存
System.out.println("\n------庫房管理人員清庫處理--------");
Stock stock = new Stock(mediator);
stock.clearStock();
}
}
------采購人員采購電腦--------
IBM電腦的銷售情況為:52
采購IBM電腦:50臺------銷售人員銷售電腦--------
銷售IBM電腦1臺
庫存數(shù)量為:99------庫房管理人員清庫處理--------
清理存貨數(shù)量為:99
折價銷售IBM電腦99臺
不再采購IBM電腦
在場景類中增加了一個中介者,然后分別傳遞到三個同事類中,三個類都具有相同的特性:只負(fù)責(zé)處理自己的活動(行為),與自己無關(guān)的活動就丟給中介者處理,程序運(yùn)行的結(jié)果是相同的。從項(xiàng)目設(shè)計上來看,加入了中介者,設(shè)計結(jié)構(gòu)清晰了很多,而且類間的耦合性大大減少,代碼質(zhì)量也有了很大的提升。在多個對象依賴的情況下,通過加入中介者角色,取消了多個對象的關(guān)聯(lián)或依賴關(guān)系,減少了對象的耦合性。
嗯!這次就比較完美了。。我們的任務(wù)也就結(jié)束了。
中介者模式的優(yōu)點(diǎn)
中介者模式的優(yōu)點(diǎn)就是減少類間的依賴,把原有的一對多的依賴變成了一對一的依賴,同事類只依賴中介者,減少了依賴,當(dāng)然同時也降低了類間的耦合。
中介者模式的缺點(diǎn)
中介者模式的缺點(diǎn)就是中介者會膨脹得很大,而且邏輯復(fù)雜,原本N個對象直接的相互依賴關(guān)系轉(zhuǎn)換為中介者和同事類的依賴關(guān)系,同事類越多,中介者的邏輯就越復(fù)雜。
中介者模式的使用場景
中介者模式簡單,但是簡單不代表容易使用,很容易被誤用。在面向?qū)ο蟮木幊讨?,對象和對象之間必然會有依賴關(guān)系,如果某個類和其他類沒有任何相互依賴的關(guān)系,那這個類就是一個“孤島”,在項(xiàng)目中就沒有存在的必要了!就像是某個人如果永遠(yuǎn)獨(dú)立生活,與任何人都沒有關(guān)系,那這個人基本上就算是野人了——排除在人類這個定義之外。類之間的依賴關(guān)系是必然存在的,一個類依賴多個類的情況也是存在的,存在即合理,那是否可以說只要有多個依賴關(guān)系就考慮使用中介者模式呢?答案是否定的。中介者模式未必能幫你把原本凌亂的邏輯整理得清清楚楚,而且中介者模式也是有缺點(diǎn)的,這個缺點(diǎn)在使用不當(dāng)時會被放大,比如原本就簡單的幾個對象依賴關(guān)系,如果為了使用模式而加入了中介者,必然導(dǎo)致中介者的邏輯復(fù)雜化,因此中介者模式的使用需要“量力而行”!中介者模式適用于多個對象之間緊密耦合的情況,緊密耦合的標(biāo)準(zhǔn)是:在類圖中出現(xiàn)了蜘蛛網(wǎng)狀結(jié)構(gòu)。在這種情況下一定要考慮使用中介者模式,這有利于把蜘蛛網(wǎng)梳理為星型結(jié)構(gòu),使原本復(fù)雜混亂的關(guān)系變得清晰簡單。