JAVA设计模式(22) —<行为型>中介者模式(Mediator)

来源:互联网 发布:node sass 安装 编辑:程序博客网 时间:2024/06/05 17:48

定义:

中介者模式(Mediator)

Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.(用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。)

1.1 通用类图:


    在中介者模式结构图中包含如下几个角色:

       Mediator(抽象中介者)它定义一个接口,该接口用于与各同事对象之间进行通信。

       ConcreteMediator(具体中介者)它是抽象中介者的子类,通过协调各个同事对象来实现协作行为,它维持了对各个同事对象的引用。

       Colleague(抽象同事类)它定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时它维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。

       ConcreteColleague(具体同事类)它是抽象同事类的子类;每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信;在具体同事类中实现了在抽象同事类中声明的抽象方法。

 

      中介者模式的核心在于中介者类的引入,在中介者模式中,中介者类承担了两方面的职责:

       (1) 中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,可通过中介者来实现间接调用。该中转作用属于中介者在结构上的支持。

      (2) 协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致的和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。


1.2中介模式的作用

如果在一个系统中对象之间的联系呈现为网状结构,如图20-4所示。对象之间存在大量的多对多联系,将导致系统非常复杂,这些对象既会影响别的对象,也会被别的对象所影响,这些对象称为同事对象,它们之间通过彼此的相互作用实现系统的行为。在网状结构中,几乎每个对象都需要与其他对象发生相互作用,而这种相互作用表现为一个对象与另外一个对象的直接耦合,这将导致一个过度耦合的系统。

20-4 对象之间存在复杂关系的网状结构

      中介者模式可以使对象之间的关系数量急剧减少,通过引入中介者对象,可以将系统的网状结构变成以中介者为中心的星形结构,如图20-5所示。在这个星形结构中,同事对象不再直接与另一个对象联系,它通过中介者对象与另一个对象发生相互作用。中介者对象的存在保证了对象结构上的稳定,也就是说,系统的结构不会因为新对象的引入带来大量的修改工作。

20-5 引入中介者对象的星型结构

      如果在一个系统中对象之间存在多对多的相互关系,我们可以将对象之间的一些交互行为从各个对象中分离出来,并集中封装在一个中介者对象中,并由该中介者进行统一协调,这样对象之间多对多的复杂关系就转化为相对简单的一对多关系。通过引入中介者来简化对象之间的复杂交互,中介者模式是“迪米特法则”的一个典型应用


1.3 通用代码:

[java] view plaincopy
  1. public abstract class Mediator {  
  2.     // 定义同事类  
  3.     protected ConcreteColleague1 c1;  
  4.     protected ConcreteColleague2 c2;  
  5.   
  6.     // 通过getter/setter方法吧同事类注入进来  
  7.     public ConcreteColleague1 getC1() {  
  8.         return c1;  
  9.     }  
  10.   
  11.     public void setC1(ConcreteColleague1 c1) {  
  12.         this.c1 = c1;  
  13.     }  
  14.   
  15.     public ConcreteColleague2 getC2() {  
  16.         return c2;  
  17.     }  
  18.   
  19.     public void setC2(ConcreteColleague2 c2) {  
  20.         this.c2 = c2;  
  21.     }  
  22.   
  23.     // 中介者模式的业务逻辑  
  24.     public abstract void doSomething1();  
  25.   
  26.     public abstract void doSomething2();  
  27. }  
  28.   
  29. public class ConcreteMediator extends Mediator {  
  30.     @Override  
  31.     public void doSomething1() {  
  32.         // 调用同事类的方法,只要是public方法都可以调用  
  33.         super.c1.selfMethod1();  
  34.         super.c2.selfMethod2();  
  35.     }  
  36.   
  37.     public void doSomething2() {  
  38.         super.c1.selfMethod1();  
  39.         super.c2.selfMethod2();  
  40.     }  
  41. }  
  42.   
  43. public abstract class Colleague {  
  44.     protected Mediator mediator;  
  45.   
  46.     public Colleague(Mediator _mediator) {  
  47.         this.mediator = _mediator;  
  48.     }  
  49. }  
  50.   
  51. public class ConcreteColleague1 extends Colleague {  
  52.     // 通过构造函数传递中介者  
  53.     public ConcreteColleague1(Mediator _mediator) {  
  54.         super(_mediator);  
  55.     }  
  56.   
  57.     // 自有方法 self-method  
  58.     public void selfMethod1() {  
  59.         // 处理自己的业务逻辑  
  60.     }  
  61.   
  62.     // 依赖方法 dep-method  
  63.     public void depMethod1() {  
  64.         // 处理自己的业务逻辑  
  65.         // 自己不能处理的业务逻辑,委托给中介者处理  
  66.         super.mediator.doSomething1();  
  67.     }  
  68. }  
  69.   
  70. public class ConcreteColleague2 extends Colleague {  
  71.     // 通过构造函数传递中介者  
  72.     public ConcreteColleague2(Mediator _mediator) {  
  73.         super(_mediator);  
  74.     }  
  75.   
  76.     // 自有方法 self-method  
  77.     public void selfMethod2() {  
  78.         // 处理自己的业务逻辑  
  79.     }  
  80.   
  81.     // 依赖方法 dep-method  
  82.     public void depMethod2() {  
  83.         // 处理自己的业务逻辑  
  84.         // 自己不能处理的业务逻辑,委托给中介者处理  
  85.         super.mediator.doSomething2();  
  86.     }  
  87. }  

注意:

Mediator抽象类中我们只定义了同事类的get/set注入,为什么不使用抽象注入呢?这是因为同事类虽然有抽象,但是没有每个同事类必须要完成的业务方法。当然如果每个同事类都有相同的方法,比如execute handler等,那当然注入抽象类,做到依赖倒置。

为什么同事类要使用构造函数注入中介者,而中介者注入同事类只使用getter/setter方式呢?因为同事类必须有中介者,而中介者却可以只有部分同事类

优点

减少类间的依赖,把原有的一对多的依赖变成了一对一依赖,同事类只依赖中介者,减少了依赖,当然同时也降低了类间的耦合。

缺点

中介者会膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。

应用场景

类之间的联系是必然的,并非有多个依赖时就要使用中介者模式。中介者模式只适用于多个对象间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构。在这种情况下一定要考虑使用中介者模式,这有利于把蜘蛛网梳理为星型结构,使原本复杂混乱的关系变得清晰简单。

4.1 符合上述紧密耦合标准时使用;

4.2 N个对象之间产生了相互的依赖关系(N>2);

4.3 多个对象有依赖关系,但是依赖的行为沿不确定或者有发生改变的可能,在这种情况下一般建议采用中介者模式,降低变更引起的风险扩散;

4.4 产品开发。一个明显的例子就是MVC框架,把中介者模式应用到产品中,可以提升产品的性能和扩展性,但是对于项目开发就未必,因为项目是以交付投产为目标,而产品则是以稳定、高效、扩展为宗旨。

生活中的使用情境:

A。机场调度中心;

BMVC框架;

C。媒体网关;(如MSN通过服务器转发消息,而飞鸽直接使用UDP广播却不是。)

D。各种中介服务;

注意事项

5.1 遵循紧密耦合的标准来用中介者模式;

5.2 中介者模式很少用到接口或抽象类,这与依赖倒置原则是冲突的:首先,既然是同事类而不是兄弟类(有相同的血缘),那就说明这些类之间是协作关系,完成不同的任务,处理不同的业务,所以不能在抽象类或接口中严格定义同事类必须具有的方法。因此,如果两个对象不能提炼出共性,就不要刻意去追求两者的抽象,抽象只要定义出模式需要的角色即可

其次,在一个项目中使用中介者模式可能被多个模块采用,每个中介者所围绕的同事类各不相同,抽象出一个具有共性的中介者也不太可能。

5.3 中介者模式是一种比较常用的模式,也是一种比较容易被滥用的模式。对于大多数的情况,同事类之间的关系不会复杂到混乱不堪的网状结构,因此,大多数情况下,将对象间的依赖关系封装的同事类内部就可以的,没有必要非引入中介者模式。滥用中介者模式,只会让事情变的更复杂

扩展

中介者模式将一个网状的系统结构变成一个以中介者对象为中心的星形结构,在这个星型结构中,使用中介者对象与其他对象的一对多关系来取代原有对象之间的多对多关系。中介者模式在事件驱动类软件中应用较为广泛,特别是基于GUIGraphical User Interface,图形用户界面)的应用软件,此外,在类与类之间存在错综复杂的关联关系的系统中,中介者模式都能得到较好的应用。

范例

7.1 进销存案例

这三者间已形成了网状关系,加入了一个中介者作为三个模块的交流核心每个模块之间不再相互交流,要交流就通过中介者进行。每个模块只负责自己的业务逻辑,不属于自己的则丢给中介者来处理,简化了各模块之间的耦合关系,类图如下:


[java] view plaincopy
  1.     /** 
  2.      * @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you 
  3.      *         all. 
  4.      */  
  5.     public abstract class AbstractMediator {  
  6.         protected Purchase purchase;  
  7.         protected Sale sale;  
  8.         protected Stock stock;  
  9.   
  10.         // 构造函数  
  11.         public AbstractMediator() {  
  12.             purchase = new Purchase(this);  
  13.             sale = new Sale(this);  
  14.             stock = new Stock(this);  
  15.         }  
  16.   
  17.         // 中介者最重要的方法,叫做事件方法,处理多个对象之间的关系  
  18.         public abstract void execute(String str, Object... objects);  
  19.     }  
  20.   
  21.     public class Mediator extends AbstractMediator {  
  22.         // 中介者最重要的方法  
  23.         public void execute(String str, Object... objects) {  
  24.             if (str.equals("purchase.buy")) { // 采购电脑  
  25.                 this.buyComputer((Integer) objects[0]);  
  26.             } else if (str.equals("sale.sell")) { // 销售电脑  
  27.                 this.sellComputer((Integer) objects[0]);  
  28.             } else if (str.equals("sale.offsell")) { // 折价销售  
  29.                 this.offSell();  
  30.             } else if (str.equals("stock.clear")) { // 清仓处理  
  31.                 this.clearStock();  
  32.             }  
  33.         }  
  34.   
  35.         // 采购电脑  
  36.         private void buyComputer(int number) {  
  37.             int saleStatus = super.sale.getSaleStatus();  
  38.             if (saleStatus > 80) { // 销售情况良好  
  39.                 System.out.println("采购IBM电脑:" + number + "台");  
  40.                 super.stock.increase(number);  
  41.             } else { // 销售情况不好  
  42.                 int buyNumber = number / 2// 折半采购  
  43.                 System.out.println("采购IBM电脑:" + buyNumber + "台");  
  44.             }  
  45.         }  
  46.   
  47.         // 销售电脑  
  48.         private void sellComputer(int number) {  
  49.             if (super.stock.getStockNumber() < number) { // 库存数量不够销售  
  50.                 super.purchase.buyIBMcomputer(number);  
  51.             }  
  52.             super.stock.decrease(number);  
  53.         }  
  54.   
  55.         // 折价销售电脑  
  56.         private void offSell() {  
  57.             System.out.println("折价销售IBM电脑" + stock.getStockNumber() + "台");  
  58.         }  
  59.   
  60.         // 清仓处理  
  61.         private void clearStock() {  
  62.             // 要求清仓销售  
  63.             super.sale.offSale();  
  64.             // 要求采购人员不要采购  
  65.             super.purchase.refuseBuyIBM();  
  66.         }  
  67.     }  

中介者Mediator定义了多个private方法,其目的是处理各个对象之间的依赖关系,就是说把原有一个对象要依赖的多个对象的情况移动到中介者的private方法中实现

但在实际项目中,一般的做法是中介者按照职责进行划分,每个中介者处理一个或多个类似的关联请求。

[java] view plaincopy
  1. public abstract class AbstractColleague {  
  2.         protected AbstractMediator mediator;  
  3.   
  4.         public AbstractColleague(AbstractMediator _mediator) {  
  5.             this.mediator = _mediator;  
  6.         }  
  7.     }  
  8.   
  9.     public class Purchase extends AbstractColleague {  
  10.         public Purchase(AbstractMediator _mediator) {  
  11.             super(_mediator);  
  12.         }  
  13.   
  14.         // 采购IBM型号的电脑  
  15.         public void buyIBMcomputer(int number) {  
  16.             super.mediator.execute("purchase.buy", number);  
  17.         }  
  18.   
  19.         // 不在采购IBM电脑  
  20.         public void refuseBuyIBM() {  
  21.             System.out.println("不再采购IBM电脑");  
  22.         }  
  23.     }  
  24.   
  25.     public class Stock extends AbstractColleague {  
  26.         public Stock(AbstractMediator _mediator) {  
  27.             super(_mediator);  
  28.         }  
  29.   
  30.         // 刚开始有100台电脑  
  31.         private static int COMPUTER_NUMBER = 100;  
  32.   
  33.         // 库存增加  
  34.         public void increase(int number) {  
  35.             COMPUTER_NUMBER = COMPUTER_NUMBER + number;  
  36.             System.out.println("库存数量为:" + COMPUTER_NUMBER);  
  37.         }  
  38.   
  39.         // 库存降低  
  40.         public void decrease(int number) {  
  41.             COMPUTER_NUMBER = COMPUTER_NUMBER - number;  
  42.             System.out.println("库存数量为:" + COMPUTER_NUMBER);  
  43.         }  
  44.   
  45.         // 获得库存数量  
  46.         public int getStockNumber() {  
  47.             return COMPUTER_NUMBER;  
  48.         }  
  49.   
  50.         // 存货压力大了,就要通知采购人员不要采购,销售人员要尽快销售  
  51.         public void clearStock() {  
  52.             System.out.println("清理存货数量为:" + COMPUTER_NUMBER);  
  53.             super.mediator.execute("stock.clear");  
  54.         }  
  55.     }  
  56.   
  57.     public class Sale extends AbstractColleague {  
  58.         public Sale(AbstractMediator _mediator) {  
  59.             super(_mediator);  
  60.         }  
  61.   
  62.         // 销售IBM型号的电脑  
  63.         public void sellIBMComputer(int number) {  
  64.             super.mediator.execute("sale.sell", number);  
  65.             System.out.println("销售IBM电脑" + number + "台");  
  66.         }  
  67.   
  68.         // 反馈销售情况,0——100之间变化,0代表根本就没人卖,100代表非常畅销,出1一个卖一个  
  69.         public int getSaleStatus() {  
  70.             Random rand = new Random(System.currentTimeMillis());  
  71.             int saleStatus = rand.nextInt(100);  
  72.             System.out.println("IBM电脑的销售情况为:" + saleStatus);  
  73.             return saleStatus;  
  74.         }  
  75.   
  76.         // 折价处理  
  77.         public void offSale() {  
  78.             super.mediator.execute("sale.offsell");  
  79.         }  
  80.     }  
  81.   
  82.     public class Client {  
  83.         public static void main(String[] args) {  
  84.             AbstractMediator mediator = new Mediator();  
  85.             // 采购人员采购电脑  
  86.             System.out.println("------采购人员采购电脑--------");  
  87.             Purchase purchase = new Purchase(mediator);  
  88.             purchase.buyIBMcomputer(100);  
  89.             // 销售人员销售电脑  
  90.             System.out.println("\n------销售人员销售电脑--------");  
  91.             Sale sale = new Sale(mediator);  
  92.             sale.sellIBMComputer(1);  
  93.             // 库房管理人员管理库存  
  94.             System.out.println("\n------库房管理人员清库处理--------");  
  95.             Stock stock = new Stock(mediator);  
  96.             stock.clearStock();  
  97.         }  
  98.     }  

在场景类中增加了一个中介者,然后分别传递到三个同事类中,三个类都具有相同的特性:只负责处理自己的活动(行为),与自己无关的活动就丢给中介者处理。

7.2租房案例

以租房为例,这里中介机构充当租房者与房屋所有者之间的中介者。UML结构图:


       首先是抽象中介者:Mediator.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public abstract class Mediator {  
  2.     //申明一个联络方法  
  3.     public abstract void constact(String message,Person person);  
  4. }  

       然后是抽象同事对象:Person.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public abstract class Person {  
  2.     protected String name;  
  3.     protected Mediator mediator;  
  4.       
  5.     Person(String name,Mediator mediator){  
  6.         this.name = name;  
  7.         this.mediator = mediator;  
  8.     }  
  9.       
  10. }  

       两个具体同事类:HouseOwner.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class HouseOwner extends Person{  
  2.   
  3.     HouseOwner(String name, Mediator mediator) {  
  4.         super(name, mediator);  
  5.     }  
  6.       
  7.     /** 
  8.      * @desc 与中介者联系 
  9.      * @param message 
  10.      * @return void 
  11.      */  
  12.     public void constact(String message){  
  13.         mediator.constact(message, this);  
  14.     }  
  15.   
  16.     /** 
  17.      * @desc 获取信息 
  18.      * @param message 
  19.      * @return void 
  20.      */  
  21.     public void getMessage(String message){  
  22.         System.out.println("房主:" + name +",获得信息:" + message);  
  23.     }  
  24. }  

       Tenant.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class Tenant extends Person{  
  2.       
  3.     Tenant(String name, Mediator mediator) {  
  4.         super(name, mediator);  
  5.     }  
  6.       
  7.     /** 
  8.      * @desc 与中介者联系 
  9.      * @param message 
  10.      * @return void 
  11.      */  
  12.     public void constact(String message){  
  13.         mediator.constact(message, this);  
  14.     }  
  15.   
  16.     /** 
  17.      * @desc 获取信息 
  18.      * @param message 
  19.      * @return void 
  20.      */  
  21.     public void getMessage(String message){  
  22.         System.out.println("租房者:" + name +",获得信息:" + message);  
  23.     }  
  24. }  

       具体中介者对象:中介结构、MediatorStructure.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class MediatorStructure extends Mediator{  
  2.     //首先中介结构必须知道所有房主和租房者的信息  
  3.     private HouseOwner houseOwner;  
  4.     private Tenant tenant;  
  5.   
  6.     public HouseOwner getHouseOwner() {  
  7.         return houseOwner;  
  8.     }  
  9.   
  10.     public void setHouseOwner(HouseOwner houseOwner) {  
  11.         this.houseOwner = houseOwner;  
  12.     }  
  13.   
  14.     public Tenant getTenant() {  
  15.         return tenant;  
  16.     }  
  17.   
  18.     public void setTenant(Tenant tenant) {  
  19.         this.tenant = tenant;  
  20.     }  
  21.   
  22.     public void constact(String message, Person person) {  
  23.         if(person == houseOwner){          //如果是房主,则租房者获得信息  
  24.             tenant.getMessage(message);  
  25.         }  
  26.         else{       //反正则是房主获得信息  
  27.             houseOwner.getMessage(message);  
  28.         }  
  29.     }  
  30. }  

       客户端:Client.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class Client {  
  2.     public static void main(String[] args) {  
  3.         //一个房主、一个租房者、一个中介机构  
  4.         MediatorStructure mediator = new MediatorStructure();  
  5.           
  6.         //房主和租房者只需要知道中介机构即可  
  7.         HouseOwner houseOwner = new HouseOwner("张三", mediator);  
  8.         Tenant tenant = new Tenant("李四", mediator);  
  9.           
  10.         //中介结构要知道房主和租房者  
  11.         mediator.setHouseOwner(houseOwner);  
  12.         mediator.setTenant(tenant);  
  13.           
  14.         tenant.constact("听说你那里有三室的房主出租.....");  
  15.         houseOwner.constact("是的!请问你需要租吗?");  
  16.     }  
  17. }  

       运行结果:

       房主:张三,获得信息:听说你那里有三室的房主出租..... 

       租房者:李四,获得信息:是的!请问你需要租吗?

7.3管理页面UI组件管理

  Sunny软件公司欲开发一套CRM系统,其中包含一个客户信息管理模块,所设计的“客户信息管理窗口”界面效果图如图20-2所示:

20-2 “客户信息管理窗口”界面图

       Sunny公司开发人员通过分析发现,在图20-2中,界面组件之间存在较为复杂的交互关系:如果删除一个客户,要在客户列表(List)中删掉对应的项,客户选择组合框(ComboBox)中客户名称也将减少一个;如果增加一个客户信息,客户列表中需增加一个客户,且组合框中也将增加一项。

       如果实现界面组件之间的交互是Sunny公司开发人员必须面对的一个问题?

        Sunny公司开发人员对组件之间的交互关系进行了分析,结果如下:

       (1) 当用户单击“增加”按钮、“删除”按钮、“修改”按钮或“查询”按钮时,界面左侧的“客户选择组合框”、“客户列表”以及界面中的文本框将产生响应。

       (2) 当用户通过“客户选择组合框”选中某个客户姓名时,“客户列表”和文本框将产生响应。

       (3) 当用户通过“客户列表”选中某个客户姓名时,“客户选择组合框”和文本框将产生响应。

      于是,Sunny公司开发人员根据组件之间的交互关系绘制了如图20-3所示初始类图:

20-3 “客户信息管理窗口”原始类图

      与类图20-3所对应的框架代码片段如下:

[java] view plaincopy
  1. //按钮类  
  2. class Button {  
  3.     private List list;  
  4.     private ComboBox cb;  
  5.     private TextBox tb;  
  6.     ......  
  7.   
  8.     //界面组件的交互  
  9.     public void change() {  
  10.         list.update();  
  11.         cb.update();  
  12.         tb.update();  
  13.     }  
  14.   
  15.     public void update() {  
  16.         ......  
  17.     }  
  18.     ......  
  19. }  
  20.   
  21. //列表框类  
  22. class List {  
  23.     private ComboBox cb;  
  24.     private TextBox tb;  
  25.     ......  
  26.   
  27. //界面组件的交互  
  28.     public void change() {  
  29.         cb.update();  
  30.         tb.update();  
  31.     }  
  32.   
  33.     public void update() {  
  34.         ......  
  35.     }  
  36.     ......    
  37. }  
  38.   
  39. //组合框类  
  40. class ComboBox {  
  41.     private List list;  
  42.     private TextBox tb;  
  43.     ......  
  44.   
  45. //界面组件的交互  
  46.     public void change() {  
  47.         list.update();  
  48.         tb.update();  
  49.     }  
  50.   
  51.     public void update() {  
  52.         ......  
  53.     }  
  54.     ......    
  55. }  
  56.   
  57. //文本框类  
  58. class TextBox {  
  59.     public void update() {  
  60.         ......  
  61.     }  
  62.     ......    
  63. }  

        分析图20-3所示初始结构图和上述代码,我们不难发现该设计方案存在如下问题:

       (1) 系统结构复杂且耦合度高:每一个界面组件都与多个其他组件之间产生相互关联和调用,若一个界面组件对象发生变化,需要跟踪与之有关联的其他所有组件并进行处理,系统组件之间呈现一种较为复杂的网状结构,组件之间的耦合度高。

       (2) 组件的可重用性差:由于每一个组件和其他组件之间都具有很强的关联,若没有其他组件的支持,一个组件很难被另一个系统或模块重用,这些组件表现出来更像一个不可分割的整体,而在实际使用时,我们往往需要每一个组件都能够单独重用,而不是重用一个由多个组件组成的复杂结构。

       (3) 系统的可扩展性差:如果在上述系统中增加一个新的组件类,则必须修改与之交互的其他组件类的源代码,将导致多个类的源代码需要修改,同样,如果要删除一个组件也存在类似的问题,这违反了“开闭原则”,可扩展性和灵活性欠佳。

       由于存在上述问题,Sunny公司开发人员不得不对原有系统进行重构,那如何重构呢?大家想到了“迪米特法则”,引入一个“第三者”来降低现有系统中类之间的耦合度。由这个“第三者”来封装并协调原有组件两两之间复杂的引用关系,使之成为一个松耦合的系统,这个“第三者”又称为“中介者”,中介者模式因此而得名。下面让我们正式进入中介者模式的学习,学会如何使用中介者类来协调多个类/对象之间的交互,以达到降低系统耦合度的目的。

完整解决方案

为了协调界面组件对象之间的复杂交互关系,Sunny公司开发人员使用中介者模式来设计客户信息管理窗口,其结构示意图如图20-7所示:

20-7 引入了中介者类的“客户信息管理窗口”结构示意图

      20-7只是一个重构之后的结构示意图,在具体实现时,为了确保系统具有更好的灵活性和可扩展性,我们需要定义抽象中介者和抽象组件类,其中抽象组件类是所有具体组件类的公共父类,完整类图如图20-8所示:

20-8 重构后的“客户信息管理窗口”结构图

      在图20-8中,Component充当抽象同事类,ButtonListComboBoxTextBox充当具体同事类,Mediator充当抽象中介者类,ConcreteMediator充当具体中介者类,ConcreteMediator维持了对具体同事类的引用,为了简化ConcreteMediator类的代码,我们在其中只定义了一个Button对象和一个TextBox对象。完整代码如下所示:

[java] view plaincopy
  1. //抽象中介者  
  2. abstract class Mediator {  
  3.     public abstract void componentChanged(Component c);  
  4. }  
  5.   
  6. //具体中介者  
  7. class ConcreteMediator extends Mediator {  
  8.     //维持对各个同事对象的引用  
  9.     public Button addButton;  
  10.     public List list;  
  11.     public TextBox userNameTextBox;  
  12.     public ComboBox cb;  
  13.   
  14.     //封装同事对象之间的交互  
  15.     public void componentChanged(Component c) {  
  16.         //单击按钮  
  17. if(c == addButton) {  
  18.             System.out.println("--单击增加按钮--");  
  19.             list.update();  
  20.             cb.update();  
  21.             userNameTextBox.update();  
  22.         }  
  23.         //从列表框选择客户  
  24.         else if(c == list) {  
  25.             System.out.println("--从列表框选择客户--");  
  26.             cb.select();  
  27.             userNameTextBox.setText();  
  28.         }  
  29.         //从组合框选择客户  
  30.         else if(c == cb) {  
  31.             System.out.println("--从组合框选择客户--");  
  32.             cb.select();  
  33.             userNameTextBox.setText();  
  34.         }  
  35.     }  
  36. }  
  37.   
  38. //抽象组件类:抽象同事类  
  39. abstract class Component {  
  40.     protected Mediator mediator;  
  41.       
  42.     public void setMediator(Mediator mediator) {  
  43.         this.mediator = mediator;  
  44.     }  
  45.   
  46.     //转发调用  
  47.     public void changed() {  
  48.         mediator.componentChanged(this);  
  49.     }  
  50.       
  51.     public abstract void update();    
  52. }  
  53.   
  54. //按钮类:具体同事类  
  55. class Button extends Component {  
  56.     public void update() {  
  57.         //按钮不产生交互  
  58.     }  
  59. }  
  60.   
  61. //列表框类:具体同事类  
  62. class List extends Component {  
  63.     public void update() {  
  64.         System.out.println("列表框增加一项:张无忌。");  
  65.     }  
  66.       
  67.     public void select() {  
  68.         System.out.println("列表框选中项:小龙女。");  
  69.     }  
  70. }  
  71.   
  72. //组合框类:具体同事类  
  73. class ComboBox extends Component {  
  74.     public void update() {  
  75.         System.out.println("组合框增加一项:张无忌。");  
  76.     }  
  77.       
  78.     public void select() {  
  79.         System.out.println("组合框选中项:小龙女。");  
  80.     }  
  81. }  
  82.   
  83. //文本框类:具体同事类  
  84. class TextBox extends Component {  
  85.     public void update() {  
  86.         System.out.println("客户信息增加成功后文本框清空。");  
  87.     }  
  88.       
  89.     public void setText() {  
  90.         System.out.println("文本框显示:小龙女。");  
  91.     }  
  92. }  

      编写如下客户端测试代码:

[java] view plaincopy
  1. class Client {  
  2.     public static void main(String args[]) {  
  3.         //定义中介者对象  
  4.         ConcreteMediator mediator;  
  5.         mediator = new ConcreteMediator();  
  6.           
  7.         //定义同事对象  
  8.         Button addBT = new Button();  
  9.         List list = new List();  
  10.         ComboBox cb = new ComboBox();  
  11.         TextBox userNameTB = new TextBox();  
  12.   
  13.         addBT.setMediator(mediator);  
  14.         list.setMediator(mediator);  
  15.         cb.setMediator(mediator);  
  16.         userNameTB.setMediator(mediator);  
  17.   
  18.         mediator.addButton = addBT;  
  19.         mediator.list = list;  
  20.         mediator.cb = cb;  
  21.         mediator.userNameTextBox = userNameTB;  
  22.           
  23.         addBT.changed();  
  24.         System.out.println("-----------------------------");  
  25.         list.changed();  
  26.     }  
  27. }  

      编译并运行程序,输出结果如下:

--单击增加按钮--

列表框增加一项:张无忌。

组合框增加一项:张无忌。

客户信息增加成功后文本框清空。

-----------------------------

--从列表框选择客户--

组合框选中项:小龙女。

文本框显示:小龙女。

中介者与同事类的扩展

  Sunny软件公司CRM系统的客户对“客户信息管理窗口”提出了一个修改意见:要求在窗口的下端能够及时显示当前系统中客户信息的总数。修改之后的界面如图20-9所示:

20-9 修改之后的“客户信息管理窗口”界面图

       从图20-9中我们不难发现,可以通过增加一个文本标签(Label)来显示客户信息总数,而且当用户点击“增加”按钮或者“删除”按钮时,将改变文本标签的内容。

      由于使用了中介者模式,在原有系统中增加新的组件(即新的同事类)将变得很容易,我们至少有如下两种解决方案:

       【解决方案一】增加一个界面组件类Label,修改原有的具体中介者类ConcreteMediator,增加一个对Label对象的引用,然后修改componentChanged()方法中其他相关组件对象的业务处理代码,原有组件类无须任何修改,客户端代码也需针对新增组件Label进行适当修改。

       【解决方案二】与方案一相同,首先增加一个Label类,但不修改原有具体中介者类ConcreteMediator的代码,而是增加一个ConcreteMediator的子类SubConcreteMediator来实现对Label对象的引用,然后在新增的中介者类SubConcreteMediator中通过覆盖componentChanged()方法来实现所有组件(包括新增Label组件)之间的交互,同样,原有组件类无须做任何修改,客户端代码需少许修改。

      引入Label之后“客户信息管理窗口”类结构示意图如图20-10所示:

20-10 增加Label组件类后的“客户信息管理窗口”结构示意图

       由于【解决方案二】无须修改ConcreteMediator类,更符合“开闭原则”,因此我们选择该解决方案来对新增Label类进行处理,对应的完整类图如图20-11所示:

20-11 修改之后的“客户信息管理窗口”结构图

      在图20-11中,新增了具体同事类Label和具体中介者类SubConcreteMediator,代码如下所示:

[java] view plaincopy
  1. //文本标签类:具体同事类  
  2. class Label extends Component {  
  3.     public void update() {  
  4.         System.out.println("文本标签内容改变,客户信息总数加1。");  
  5.     }  
  6. }  
  7.   
  8. //新增具体中介者类  
  9. class SubConcreteMediator extends ConcreteMediator {  
  10.     //增加对Label对象的引用  
  11.     public Label label;  
  12.       
  13.     public void componentChanged(Component c) {  
  14.         //单击按钮  
  15. if(c == addButton) {  
  16.             System.out.println("--单击增加按钮--");  
  17.             list.update();  
  18.             cb.update();  
  19.             userNameTextBox.update();  
  20.             label.update(); //文本标签更新  
  21.         }  
  22.         //从列表框选择客户  
  23.         else if(c == list) {  
  24.             System.out.println("--从列表框选择客户--");  
  25.             cb.select();  
  26.             userNameTextBox.setText();  
  27.         }  
  28.         //从组合框选择客户  
  29.         else if(c == cb) {  
  30.             System.out.println("--从组合框选择客户--");  
  31.             cb.select();  
  32.             userNameTextBox.setText();  
  33.         }  
  34.     }  
  35. }  

       修改客户端测试代码:

[java] view plaincopy
  1. class Client {  
  2.     public static void main(String args[]) {  
  3.         //用新增具体中介者定义中介者对象  
  4.         SubConcreteMediator mediator;  
  5.         mediator = new SubConcreteMediator();  
  6.           
  7.         Button addBT = new Button();  
  8.         List list = new List();  
  9.         ComboBox cb = new ComboBox();  
  10.         TextBox userNameTB = new TextBox();  
  11.         Label label = new Label();  
  12.   
  13.         addBT.setMediator(mediator);  
  14.         list.setMediator(mediator);  
  15.         cb.setMediator(mediator);  
  16.         userNameTB.setMediator(mediator);  
  17.         label.setMediator(mediator);  
  18.           
  19.         mediator.addButton = addBT;  
  20.         mediator.list = list;  
  21.         mediator.cb = cb;  
  22.         mediator.userNameTextBox = userNameTB;  
  23.         mediator.label = label;  
  24.               
  25.         addBT.changed();  
  26.         System.out.println("-----------------------------");  
  27.         list.changed();  
  28.     }  
  29. }  

       编译并运行程序,输出结果如下:

--单击增加按钮--

列表框增加一项:张无忌。

组合框增加一项:张无忌。

客户信息增加成功后文本框清空。

文本标签内容改变,客户信息总数加1

-----------------------------

--从列表框选择客户--

组合框选中项:小龙女。

文本框显示:小龙女。

       由于在本实例中不同的组件类(即不同的同事类)所拥有的方法并不完全相同,因此中介者类没有针对抽象同事类编程,导致在具体中介者类中需要维持对具体同事类的引用,客户端代码无法完全透明地对待所有同事类和中介者类。在某些情况下,如果设计得当,可以在客户端透明地对同事类和中介者类编程,这样系统将具有更好的灵活性和可扩展

 在中介者模式的实际使用过程中,如果需要引入新的具体同事类,只需要继承抽象同事类并实现其中的方法即可,由于具体同事类之间并无直接的引用关系,因此原有所有同事类无须进行任何修改,它们与新增同事对象之间的交互可以通过修改或者增加具体中介者类来实现如果需要在原有系统中增加新的具体中介者类,只需要继承抽象中介者类(或已有的具体中介者类)并覆盖其中定义的方法即可,在新的具体中介者中可以通过不同的方式来处理对象之间的交互,也可以增加对新增同事的引用和调用。在客户端中只需要修改少许代码(如果引入配置文件的话有时可以不修改任何代码)就可以实现中介者的更换。


0 0
原创粉丝点击