设计模式(十六)Mediator模式
在实际的工作小组的交流过程是,组员向仲裁者报告,仲裁者向组员下达指示,组员之间不再互相询问和指示。Mediator模式是指,当发生麻烦事情的时候,通知仲裁者;当发生涉及全体组员的事情时,也通知仲裁者。当仲裁者下达指示时,组员会立即执行。团队组员之间不再互相沟通并私自做出决定,而是发生任何事情都向仲裁者报告。另一方面,仲裁者站在整个团队的角度上对组员上报的事情作出决定。
示例程序是一个登录对话框,用户在其输入正确的用户名和密码后可以登录。
对话框有以下一些要求:
要调整多个对象之间的关系时,就需要用到Mediator模式了。即不让各个对象之间互相通信,而是增加一个仲裁者角色,让他们各自与仲裁者通信。然后,将控制显示的逻辑处理交给仲裁者。
1 public interface Mediator { 2 public abstract void createColleagues(); 3 public abstract void colleagueChanged(); 4 }
Mediator接口是表示仲裁者的接口。createColleagues方法用于生成Mediator要管理的组员,即会生成对话框中的按钮和文本输入框等控件。colleagueChanged方法的作用是让组员可以向仲裁者进行报告,即当单选按钮和文本输入框的状态发生变化时,该方法会被调用。
1 public interface Colleague { 2 public abstract void setMediator(Mediator mediator); 3 public abstract void setColleagueEnabled(boolean enabled); 4 }
Colleague接口是表示向仲裁者进行报告的组员的接口。
setMediator方法的作用是告知组员“我是仲裁者,有事情报告我”,向该方法中传递的参数是仲裁者的实例。之后在需要向仲裁者报告时(即调用colleagueChanged方法时),会调用该方法。
setColleagueEnabled方法的作用是告知组员仲裁者所下达的指示。参数enabled为true,就表明自己需要变为“启用状态”,这也表明了状态并非由组员自己决定,而是由仲裁者来决定。
1 import java.awt.Button; 2 3 public class ColleagueButton extends Button implements Colleague { 4 private Mediator mediator; 5 6 public ColleagueButton(String caption) { 7 super(caption); 8 } 9 10 public void setMediator(Mediator mediator) { // 保存Mediator 11 this.mediator = mediator; 12 } 13 14 public void setColleagueEnabled(boolean enabled) { // Mediator下达启用/禁用的指示 15 setEnabled(enabled); 16 } 17 }
ColleagueButton类中mediator字段中保存了通过setMediator方法的参数传递进来的Mediator对象的实例。
setColleagueEnabled方法会调用Button中的setEnabled方法,即setEnabled(true)后空间按钮可以被按下,否则无法被按下。
1 import java.awt.TextField; 2 import java.awt.Color; 3 import java.awt.event.TextListener; 4 import java.awt.event.TextEvent; 5 6 public class ColleagueTextField extends TextField implements TextListener, Colleague { 7 private Mediator mediator; 8 9 public ColleagueTextField(String text, int columns) { // 构造函数 10 super(text, columns); 11 } 12 13 public void setMediator(Mediator mediator) { // 保存Mediator 14 this.mediator = mediator; 15 } 16 17 public void setColleagueEnabled(boolean enabled) { // Mediator下达启用/禁用的指示 18 setEnabled(enabled); 19 setBackground(enabled ? Color.white : Color.lightGray); 20 } 21 22 public void textValueChanged(TextEvent e) { // 当文字发生变化时通知Mediator 23 mediator.colleagueChanged(); 24 } 25 }
ColleagueTextField类中textValueChanged方法是当捕捉到文本内容发生变化这一事情,就会通知给仲裁者。简单来说,当文本内容发生变化时,向仲裁者表达“对不起,文本内容有变化,请处理。”的意思。
setColleagueEnabled方法中,不仅调用setEnabled方法,还调用setBackground方法来使控件启用后背景改成白色。
1 import java.awt.Checkbox; 2 import java.awt.CheckboxGroup; 3 import java.awt.event.ItemListener; 4 import java.awt.event.ItemEvent; 5 6 public class ColleagueCheckbox extends Checkbox implements ItemListener, Colleague { 7 private Mediator mediator; 8 9 public ColleagueCheckbox(String caption, CheckboxGroup group, boolean state) { // 构造函数 10 super(caption, group, state); 11 } 12 13 public void setMediator(Mediator mediator) { // 保存Mediator 14 this.mediator = mediator; 15 } 16 17 public void setColleagueEnabled(boolean enabled) { // Mediator下达启用/禁用指示 18 setEnabled(enabled); 19 } 20 21 public void itemStateChanged(ItemEvent e) { // 当状态发生变化时通知Mediator 22 mediator.colleagueChanged(); 23 } 24 }
ColleagueCheckbox类是表示单选按钮的,同样可以通过itemStateChanged方法来捕捉单选按钮的状态变化。
1 import java.awt.Frame; 2 import java.awt.Label; 3 import java.awt.Color; 4 import java.awt.CheckboxGroup; 5 import java.awt.GridLayout; 6 import java.awt.event.ActionListener; 7 import java.awt.event.ActionEvent; 8 9 public class LoginFrame extends Frame implements ActionListener, Mediator { 10 private ColleagueCheckbox checkGuest; 11 private ColleagueCheckbox checkLogin; 12 private ColleagueTextField textUser; 13 private ColleagueTextField textPass; 14 private ColleagueButton buttonOk; 15 private ColleagueButton buttonCancel; 16 17 // 构造函数。 18 // 生成并配置各个Colleague后,显示对话框。 19 public LoginFrame(String title) { 20 super(title); 21 setBackground(Color.lightGray); 22 // 使用布局管理器生成4×2窗格 23 setLayout(new GridLayout(4, 2)); 24 // 生成各个Colleague 25 createColleagues(); 26 // 配置 27 add(checkGuest); 28 add(checkLogin); 29 add(new Label("Username:")); 30 add(textUser); 31 add(new Label("Password:")); 32 add(textPass); 33 add(buttonOk); 34 add(buttonCancel); 35 // 设置初始的启用起用/禁用状态 36 colleagueChanged(); 37 // 显示 38 pack(); 39 show(); 40 } 41 42 // 生成各个Colleague。 43 public void createColleagues() { 44 // 生成 45 CheckboxGroup g = new CheckboxGroup(); 46 checkGuest = new ColleagueCheckbox("Guest", g, true); 47 checkLogin = new ColleagueCheckbox("Login", g, false); 48 textUser = new ColleagueTextField("", 10); 49 textPass = new ColleagueTextField("", 10); 50 textPass.setEchoChar('*'); 51 buttonOk = new ColleagueButton("OK"); 52 buttonCancel = new ColleagueButton("Cancel"); 53 // 设置Mediator,事先告诉这些组员“我是仲裁者,有什么问题的可以像我报告。” 54 checkGuest.setMediator(this); 55 checkLogin.setMediator(this); 56 textUser.setMediator(this); 57 textPass.setMediator(this); 58 buttonOk.setMediator(this); 59 buttonCancel.setMediator(this); 60 // 设置Listener 61 checkGuest.addItemListener(checkGuest); 62 checkLogin.addItemListener(checkLogin); 63 textUser.addTextListener(textUser); 64 textPass.addTextListener(textPass); 65 buttonOk.addActionListener(this); 66 buttonCancel.addActionListener(this); 67 } 68 69 // 接收来自于Colleage的通知然后判断各Colleage的启用/禁用状态。 70 public void colleagueChanged() { 71 if (checkGuest.getState()) { // Guest mode 72 textUser.setColleagueEnabled(false); 73 textPass.setColleagueEnabled(false); 74 buttonOk.setColleagueEnabled(true); 75 } else { // Login mode 76 textUser.setColleagueEnabled(true); 77 userpassChanged(); 78 } 79 } 80 81 // 当textUser或是textPass文本输入框中的文字发生变化时 82 // 判断各Colleage的启用/禁用状态 83 private void userpassChanged() { 84 if (textUser.getText().length() > 0) { 85 textPass.setColleagueEnabled(true); 86 if (textPass.getText().length() > 0) { 87 buttonOk.setColleagueEnabled(true); 88 } else { 89 buttonOk.setColleagueEnabled(false); 90 } 91 } else { 92 textPass.setColleagueEnabled(false); 93 buttonOk.setColleagueEnabled(false); 94 } 95 } 96 97 public void actionPerformed(ActionEvent e) { 98 System.out.println(e.toString()); 99 System.exit(0); 100 } 101 }
colleagueChanged作为最重要的方法,首先判断单选按钮状态,如果是游客模式,那么就禁用用户名输入框和密码输入框,同时使OK按钮开启。否则就是登录模式,这里和之前提到的逻辑一致。虽然三个组员类中都有设置自身的启用/禁用状态的方法,但是并没有“具体什么情况下需要设置哪种状态”的逻辑处理。他们都只是简单地调用仲裁者的colleagueChanged方法告知仲裁者“剩下的就拜托给你了”。也就是说,所有最终的决定都是由仲裁者的colleagueChanged方法下达的。
1 import java.awt.*; 2 import java.awt.event.*; 3 4 public class Main { 5 static public void main(String args[]) { 6 new LoginFrame("Mediator Sample"); 7 } 8 }
Main方法生成了LoginFrame类的实例。虽然Main类的main方法结束了,但是LoginFrame类的实例还一直被保存在AWT框架中。