模式定义
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
- 迪米特法则(Law of Demeter,LoD)又叫作最少知识原则(Least Knowledge Principle,LKP),产生于 1987 年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出,被 UML 创始者之一的布奇(Booch)普及,后来又因为在经典著作《程序员修炼之道》(The Pragmatic Programmer)提及而广为人知。
- 迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
- 迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。
模板实现如下:
- package com.niuh.designpattern.mediator.v1;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * <p>
- * 中介者模式
- * </p>
- */
- public class MediatorPattern {
- public static void main(String[] args) {
- Mediator md = new ConcreteMediator();
- Colleague c1, c2;
- c1 = new ConcreteColleague1();
- c2 = new ConcreteColleague2();
- md.register(c1);
- md.register(c2);
- c1.send();
- System.out.println("==============");
- c2.send();
- }
- }
- //抽象中介者
- abstract class Mediator {
- public abstract void register(Colleague colleague);
- public abstract void relay(Colleague cl); //转发
- }
- //具体中介者
- class ConcreteMediator extends Mediator {
- private List<Colleague> colleagues = new ArrayList<Colleague>();
- public void register(Colleague colleague) {
- if (!colleagues.contains(colleague)) {
- colleagues.add(colleague);
- colleague.setMedium(this);
- }
- }
- public void relay(Colleague cl) {
- for (Colleague ob : colleagues) {
- if (!ob.equals(cl)) {
- ((Colleague) ob).receive();
- }
- }
- }
- }
- //抽象同事类
- abstract class Colleague {
- protected Mediator mediator;
- public void setMedium(Mediator mediator) {
- this.mediator = mediator;
- }
- public abstract void receive();
- public abstract void send();
- }
- //具体同事类
- class ConcreteColleague1 extends Colleague {
- public void receive() {
- System.out.println("具体同事类1收到请求。");
- }
- public void send() {
- System.out.println("具体同事类1发出请求。");
- mediator.relay(this); //请中介者转发
- }
- }
- //具体同事类
- class ConcreteColleague2 extends Colleague {
- public void receive() {
- System.out.println("具体同事类2收到请求。");
- }
- public void send() {
- System.out.println("具体同事类2发出请求。");
- mediator.relay(this); //请中介者转发
- }
- }
结果实现如下:
- 具体同事类1发出请求。
- 具体同事类2收到请求。
- 具体同事类2发出请求。
- 具体同事类1收到请求。
解决的问题
对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
模式组成
中介者模式实现的关键是找出“中介者”。
实例说明
实例概况
用中介者模式编写一个“北京房地产交流平台”程序。
分析:北京房地产交流平台是“房地产中介公司”提供给“卖方客户”与“买方客户”进行信息交流的平台,比较适合用中介者模式来实现。
使用步骤
步骤1:定义一个中介公司(Medium)接口,它是抽象中介者,它包含了客户注册方法 register(Customer member) 和信息转发方法 relay(String from,String ad);
- interface Medium {
- //客户注册
- void register(Customer member);
- //转发
- void relay(String from, String ad);
- }
步骤2:定义一个北京房地产中介(EstateMedium)公司,它是具体中介者类,它包含了保存客户信息的 List 对象,并实现了中介公司中的抽象方法。
- //具体中介者:房地产中介
- class EstateMedium implements Medium {
- private List<Customer> members = new ArrayList<Customer>();
- public void register(Customer member) {
- if (!members.contains(member)) {
- members.add(member);
- member.setMedium(this);
- }
- }
- public void relay(String from, String ad) {
- for (Customer ob : members) {
- String name = ob.getName();
- if (!name.equals(from)) {
- ((Customer) ob).receive(from, ad);
- }
- }
- }
- }
步骤3:定义一个客户(Qistomer)类,它是抽象同事类,其中包含了中介者的对象,和发送信息的 send(String ad) 方法与接收信息的 receive(String from,Stringad) 方法的接口,由于本程序是窗体程序,所以本类继承 JPmme 类,并实现动作事件的处理方法 actionPerformed(ActionEvent e)。
- //抽象同事类:客户
- abstract class Customer extends JFrame implements ActionListener {
- private static final long serialVersionUID = -7219939540794786080L;
- protected Medium medium;
- protected String name;
- JTextField SentText;
- JTextArea ReceiveArea;
- public Customer(String name) {
- super(name);
- this.name = name;
- }
- void ClientWindow(int x, int y) {
- Container cp;
- JScrollPane sp;
- JPanel p1, p2;
- cp = this.getContentPane();
- SentText = new JTextField(18);
- ReceiveArea = new JTextArea(10, 18);
- ReceiveArea.setEditable(false);
- p1 = new JPanel();
- p1.setBorder(BorderFactory.createTitledBorder("接收内容:"));
- p1.add(ReceiveArea);
- sp = new JScrollPane(p1);
- cp.add(sp, BorderLayout.NORTH);
- p2 = new JPanel();
- p2.setBorder(BorderFactory.createTitledBorder("发送内容:"));
- p2.add(SentText);
- cp.add(p2, BorderLayout.SOUTH);
- SentText.addActionListener(this);
- this.setLocation(x, y);
- this.setSize(250, 330);
- this.setResizable(false); //窗口大小不可调整
- this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- this.setVisible(true);
- }
- public void actionPerformed(ActionEvent e) {
- String tempInfo = SentText.getText().trim();
- SentText.setText("");
- this.send(tempInfo);
- }
- public String getName() {
- return name;
- }
- public void setMedium(Medium medium) {
- this.medium = medium;
- }
- public abstract void send(String ad);
- public abstract void receive(String from, String ad);
- }
步骤4:定义卖方(Seller)类和买方(Buyer)类,它们是具体同事类,是客户(Customer)类的子类,它们实现了父类中的抽象方法,通过中介者类进行信息交流。
- //具体同事类:卖方
- class Seller extends Customer {
- private static final long serialVersionUID = -1443076716629516027L;
- public Seller(String name) {
- super(name);
- ClientWindow(50, 100);
- }
- public void send(String ad) {
- ReceiveArea.append("我(卖方)说: " + ad + "\n");
- //使滚动条滚动到最底端
- ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
- medium.relay(name, ad);
- }
- public void receive(String from, String ad) {
- ReceiveArea.append(from + "说: " + ad + "\n");
- //使滚动条滚动到最底端
- ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
- }
- }
- //具体同事类:买方
- class Buyer extends Customer {
- private static final long serialVersionUID = -474879276076308825L;
- public Buyer(String name) {
- super(name);
- ClientWindow(350, 100);
- }
- public void send(String ad) {
- ReceiveArea.append("我(买方)说: " + ad + "\n");
- //使滚动条滚动到最底端
- ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
- medium.relay(name, ad);
- }
- public void receive(String from, String ad) {
- ReceiveArea.append(from + "说: " + ad + "\n");
- //使滚动条滚动到最底端
- ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
- }
- }
输出结果
优点
- 降低了对象之间的耦合性,使得对象易于独立地被复用。
- 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
缺点
当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。
应用场景
- 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
- 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
模式的扩展
在实际开发中,通常采用以下两种方法来简化中介者模式,使开发变得更简单。
- 不定义中介者接口,把具体中介者对象实现成为单例。
- 同事对象不持有中介者,而是在需要的时候直接获取中介者对象并调用。
程序代码如下:
- package com.niuh.designpattern.mediator.v3;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * <p>
- * 简化中介者模式
- * </p>
- */
- public class SimpleMediatorPattern {
- public static void main(String[] args) {
- SimpleColleague c1, c2;
- c1 = new SimpleConcreteColleague1();
- c2 = new SimpleConcreteColleague2();
- c1.send();
- System.out.println("==============");
- c2.send();
- }
- }
- //简单单例中介者
- class SimpleMediator {
- private static SimpleMediator smd = new SimpleMediator();
- private List<SimpleColleague> colleagues = new ArrayList<SimpleColleague>();
- private SimpleMediator() {
- }
- public static SimpleMediator getMedium() {
- return (smd);
- }
- public void register(SimpleColleague colleague) {
- if (!colleagues.contains(colleague)) {
- colleagues.add(colleague);
- }
- }
- public void relay(SimpleColleague scl) {
- for (SimpleColleague ob : colleagues) {
- if (!ob.equals(scl)) {
- ((SimpleColleague) ob).receive();
- }
- }
- }
- }
- //抽象同事类
- interface SimpleColleague {
- void receive();
- void send();
- }
- //具体同事类
- class SimpleConcreteColleague1 implements SimpleColleague {
- SimpleConcreteColleague1() {
- SimpleMediator smd = SimpleMediator.getMedium();
- smd.register(this);
- }
- public void receive() {
- System.out.println("具体同事类1:收到请求。");
- }
- public void send() {
- SimpleMediator smd = SimpleMediator.getMedium();
- System.out.println("具体同事类1:发出请求...");
- smd.relay(this); //请中介者转发
- }
- }
- //具体同事类
- class SimpleConcreteColleague2 implements SimpleColleague {
- SimpleConcreteColleague2() {
- SimpleMediator smd = SimpleMediator.getMedium();
- smd.register(this);
- }
- public void receive() {
- System.out.println("具体同事类2:收到请求。");
- }
- public void send() {
- SimpleMediator smd = SimpleMediator.getMedium();
- System.out.println("具体同事类2:发出请求...");
- smd.relay(this); //请中介者转发
- }
- }
输出结果如下:
- 具体同事类1:发出请求...
- 具体同事类2:收到请求。
- 具体同事类2:发出请求...
- 具体同事类1:收到请求。
源码中的应用
- java.util.Timer
- java.util.concurrent.Executer#execute()
- java.util.concurrent.ExecuterService#submit()
- java.lang.reflect.Method#invoke()
PS:以上代码提交在 Github :
https://github.com/Niuh-Study/niuh-designpatterns.git