設計模式:仲裁者(Mediator)模式
一、前言
Mediator模式又稱為仲裁者模式或者中介者模式,所起的作用是仲裁和中介,幫助其它類之間進行交流。在仲裁者模式之中,我們要明確兩個概念,那就是仲裁者(Mediator)和組員(Colleague),不管組員有什么事情,都會向仲裁者匯報,仲裁者會根據全局的實際情況向其他Colleague作出指示,共同完成一定的邏輯功能。

二、代碼
Mediator接口:(仲裁者接口)
1 package zyr.dp.mediator; 2 3 public interface Mediator { 4 public abstract void createColleagues(); 5 public abstract void ColleagueChanged(); 6 }
Colleague 組員接口:
1 package zyr.dp.mediator; 2 3 public interface Colleague { 4 public abstract void setMediator(Mediator mediator); 5 public abstract void setColleagueEnable(boolean enable); 6 }
ColleagueButton實現類:
1 package zyr.dp.mediator; 2 3 import java.awt.Button; 4 5 public class ColleagueButton extends Button implements Colleague { 6 7 private static final long serialVersionUID = 1309335015058337931L; 8 9 public ColleagueButton(String caption){ 10 super(caption); 11 } 12 13 private Mediator mediator; 14 public void setMediator(Mediator mediator) { 15 this.mediator=mediator; 16 } 17 18 public void setColleagueEnable(boolean enable) { 19 setEnabled(enable); 20 } 21 22 }
ColleagueCheckBox實現類:
1 package zyr.dp.mediator; 2 3 import java.awt.Checkbox; 4 import java.awt.CheckboxGroup; 5 import java.awt.event.ItemEvent; 6 import java.awt.event.ItemListener; 7 8 public class ColleagueCheckBox extends Checkbox implements ItemListener,Colleague { 9 10 private static final long serialVersionUID = -628547709974079324L; 11 12 public ColleagueCheckBox(String caption,CheckboxGroup g,boolean b){ 13 super(caption,g,b); 14 } 15 16 private Mediator mediator; 17 public void setMediator(Mediator mediator) { 18 this.mediator=mediator; 19 } 20 21 public void setColleagueEnable(boolean enable) { 22 setEnabled(enable); 23 24 } 25 26 public void itemStateChanged(ItemEvent e) { 27 mediator.ColleagueChanged(); 28 } 29 30 }
ColleagueTextField 實現類:
1 package zyr.dp.mediator; 2 3 import java.awt.Color; 4 import java.awt.TextField; 5 import java.awt.event.TextEvent; 6 import java.awt.event.TextListener; 7 8 public class ColleagueTextField extends TextField implements TextListener, Colleague { 9 10 private static final long serialVersionUID = 8539124564669846422L; 11 12 public ColleagueTextField(String text,int columns){ 13 super(text,columns); 14 } 15 16 private Mediator mediator; 17 public void setMediator(Mediator mediator) { 18 this.mediator=mediator; 19 } 20 21 public void setColleagueEnable(boolean enable) { 22 setEnabled(enable); 23 setBackground( enable? Color.WHITE : Color.BLACK ); 24 } 25 26 public void textValueChanged(TextEvent e) { 27 mediator.ColleagueChanged(); 28 } 29 30 31 }
LoginFrame 實現類:
1 package zyr.dp.mediator; 2 3 import java.awt.CheckboxGroup; 4 import java.awt.Color; 5 import java.awt.Frame; 6 import java.awt.GridLayout; 7 import java.awt.Label; 8 import java.awt.event.ActionEvent; 9 import java.awt.event.ActionListener; 10 11 public class LoginFrame extends Frame implements ActionListener , Mediator { 12 13 private static final long serialVersionUID = -509490729876024682L; 14 15 private ColleagueButton buttonOK; 16 private ColleagueButton buttonCancel; 17 18 private ColleagueCheckBox chkGuest; 19 private ColleagueCheckBox chkUser; 20 21 private ColleagueTextField tfUser; 22 private ColleagueTextField tfPass; 23 24 public LoginFrame(String title){ 25 super(title); 26 setBackground(Color.lightGray); 27 setLayout(new GridLayout(4,2)); 28 createColleagues(); 29 add(chkGuest); 30 add(chkUser); 31 add(new Label("用戶名:")); 32 add(tfUser); 33 add(new Label("密碼:")); 34 add(tfPass); 35 add(buttonOK); 36 add(buttonCancel); 37 ColleagueChanged(); 38 pack(); 39 show(); 40 } 41 42 public void createColleagues() { 43 CheckboxGroup chk=new CheckboxGroup(); 44 chkGuest=new ColleagueCheckBox("Guest",chk,true); 45 chkUser=new ColleagueCheckBox("User",chk,false); 46 buttonOK=new ColleagueButton("OK"); 47 buttonCancel=new ColleagueButton("Cancel"); 48 tfUser=new ColleagueTextField("",10); 49 tfPass=new ColleagueTextField("",10); 50 tfPass.setEchoChar('#'); 51 52 chkGuest.setMediator(this); 53 chkUser.setMediator(this); 54 buttonOK.setMediator(this); 55 buttonCancel.setMediator(this); 56 tfUser.setMediator(this); 57 tfPass.setMediator(this); 58 59 chkGuest.addItemListener(chkGuest); 60 chkUser.addItemListener(chkUser); 61 buttonOK.addActionListener(this); 62 buttonCancel.addActionListener(this); 63 tfUser.addTextListener(tfUser); 64 tfPass.addTextListener(tfPass); 65 66 } 67 68 public void ColleagueChanged() { 69 70 if(chkGuest.getState()){ 71 tfUser.setColleagueEnable(false); 72 tfPass.setColleagueEnable(false); 73 buttonOK.setColleagueEnable(true); 74 }else{ 75 tfUser.setColleagueEnable(true); 76 userPassChanged(); 77 } 78 } 79 80 private void userPassChanged() { 81 if(tfUser.getText().length()>0){ 82 tfPass.setColleagueEnable(true); 83 if(tfPass.getText().length()>0){ 84 buttonOK.setColleagueEnable(true); 85 }else{ 86 buttonOK.setColleagueEnable(false); 87 } 88 }else{ 89 tfPass.setColleagueEnable(false); 90 buttonOK.setColleagueEnable(false); 91 } 92 93 } 94 95 public void actionPerformed(ActionEvent e) { 96 System.out.println(e.toString()); 97 System.exit(0); 98 } 99 100 }
Main 方法:
1 package zyr.dp.mediator; 2 3 public class Main { 4 5 public static void main(String[] args) { 6 LoginFrame login=new LoginFrame("仲裁者模式"); 7 } 8 9 }
運行結果:
游客訪問,不用輸入用戶名和密碼,因此禁用。

會員訪問,只有輸入用戶名之后才能輸入密碼,用戶名可用,密碼暫不能用,登錄按鈕OK不可用。

會員訪問,輸入用戶名之后可以輸入密碼,用戶名可用,密碼也可用,登錄按鈕OK不可用。

會員訪問,輸入用戶名和密碼之后,登錄按鈕OK可用。

點擊登錄按鈕OK:

分析:這些組件(按鈕、文本框、單選框)每一個發生改變之后就要導致其他組件發生改變,比如用戶名文本框改變,密碼文本框也要相應改變,如果我們直接讓這兩個文本框之間產生交互,我們應該怎么樣寫代碼呢?我們可能在用戶名文本框發生改變的函數中通知密碼文本框,讓密碼文本框可用,那么我們需要能夠得到密碼文本框的對象,這必定是一件非常難以做到的事情,同樣的,密碼文本框的改變還要使得按鈕發生改變,我們勢必會將密碼文本框和按鈕之間再次建立聯系(傳遞參數),這也是一件非常復雜的事情,同樣的單選框發生改變,兩個文本框和一個按鈕都要發生改變,這又要在單選框之中得到這些組件的對象,然后才能改變這些組件的狀態,但是單選框都有兩個,來自於同一個類,我們要怎么識別呢,可能還要通過一定的方法得到單選框的ID,這是極其復雜的,最讓人難以接受的是,如果需求發生了改變,比如又增加了一個控件,剩余的所有組件的代碼都要修改,而這些代碼之間本就混亂無比,再次修改就更讓人難以接受了,因此這不是一個好的辦法,甚至可以說是一個非常差的解決辦法。
那么有什么好的解決辦法嗎?那就是將這些復雜的邏輯操作獨立出來,新創建一個類,在每個控件的狀態發生改變的時候就能通過某種方式通知到這個類,讓這個類改變這些控件的狀態,這樣說來這個新的類應該持有這些控件的引用,這樣才能委托這些控件去改變自己的狀態,同時這些控件要想通知這個新的類,那就要持有這個類(或其父類)的引用,這樣才能委托這個新的類去實現所有狀態的改變。這樣我們的思路就清晰明了了,如果一個再增加一個新的控件,需要添加邏輯,我們只需要修改仲裁者的方法,就可以做到全局的掌控,達到了修改最少的代碼來擴展程序的行為,因此仲裁者模式非常的重要和實用。
三、總結
對我們的程序而言,仲裁者模式適合於某一個部分發生改變就會導致其他部分做出相應的改變的情況,我們將這種變換的規律抽象出來,變成仲裁者,從而很好的協商各個部分,當組員有問題的時候(狀態發生改變),直接告訴仲裁者,不與其他組員打交道,讓仲裁者負責這些繁瑣的事務,這樣條理就很清晰了,因此仲裁者也成為中介者,可以想象很多人要去租房,很多人要把房子出租,如果私下里面去商量,既浪費時間,又很難找到對方,通過中介這個對象,它收集了很多的租房和出租房的信息,這樣就能很快的找到最適合的房子。由此可見生活就是最好的設計模式。仲裁者模式對於代碼的修改(很容易定位錯誤)、新的成員的加入、代碼的復用(組員部分可以復用,仲裁者部分不易復用)都有着一定的簡化,將該集中處理的集中起來,將該分散的分散出去,無疑是一種好的設計模式。對比於外觀模式Facade,仲裁者需要和組員溝通,是雙向的,而外觀模式facade角色只是對其他角色進行整合,是單向的。設計模式至今我們已經研究了16種了,這其中很多設計模式都是有着關聯的,同樣也有着不同之處,但是都和實際問題是分不開的,設計模式是實際問題的抽象和解決方法,為了提高可擴展性和組件化,實際問題是設計模式的來源和最終歸宿。
