1. 利用Mediator模式管理关系完整性:
通过应用面向对象的建模方式,我们很容易将现实世界中的对象建模为Java对象。然而,Java对象模型在反映现实世界方面仍然存在至少两个基本的缺陷:第一,现实世界中的对象可以随时间而变化,但是Java并没有为此提供支持。例如,赋值语句仅是简单地将变量以前的数值抹掉,而不能像人类那样可以记住以前的数值。第二,在现实世界中,关系和对象一样重要,但是今天的面向对象语言,如Java语句,并没有为关系提供任何支持。例如,有这样一个关系:如果压缩机2402在一号车间,那么一号车间一定含有压缩机2402;Java语言并未对描述这种关系提供内在支持。实际上,用Java表示这种关系很可能出错,因而我们需要借助Mediator模式。
下面我们考虑一下Oozinoz公司用于盛放化学品的橡胶材料箱。材料箱通常被指派给某台特定的机器,如下表所示:
材 料 箱 机 器 | |
T305 | StarPress-002 |
T308 | StarPress-002 |
T377 | ShellAssembler-2301 |
T379 | ShellAssembler-2301 |
T389 | ShellAssembler-2301 |
T001 | Fuser-2101 |
T002 | Fuser-2101 |
上表给出了材料箱与机器之间的关系----即这两者是如何相互作用的。从数学的角度来说,关系是若干个对象组成的所有有序对的子集,因而材料箱对机器存在一个关系,机器对材料箱也存在一个关系。保持材料箱数据的唯一性可以保证一个材料箱不会被同时指派给两台机器。
在对象模型中,由于有关关系的信息分散在各个被建模的对象中,因而很难保证关系的完整性。下面我们考虑一下MoveATub应用程序。该公司的设计人员最终将不再仅仅使用名称,而是开始使用Tub对象和Machine对象。当现实世界的材料箱靠近现实世界的机器时,用来代表该材料箱的对象就会引用代表该机器的对象。每个Machine对象都将有一个表示靠近该机器的材料箱的Tub对象集。下图描述了一个典型的对象模型。
在对象模型中,关系信息分散在各个对象中
上图的双向箭头强调了材料箱知道自己所属的机器,而机器也知道有哪些材料箱分配给了自己。材料箱与机器之间的关系信息现在被指派给许多对象,而不是集中记录在一个表中。材料箱与机器的这种关系分解在各个对象中,这就使得这种关系难以管理。这个时候我们就需要使用Mediator模式。
假设Oozinoz公司有一台新机器,它能够阅读材料箱上的条码,该公司的开发人员对该机器进行了错误的建模。开发人员先扫描材料箱上的条码读取材料箱ID,然后将材料箱对象t的位置设置成机器对象m。其代码如下:
//Tell tub about machine,and machine about tub
t.setMachine(m);
m.addTub(t);
突破题:假设对象的初始关系信息如下图所示,假定对象t代表材料箱T308,对象m代表机器Fuser-2101,请通过完成下图,说明上述材料箱位置更新代码的执行效果。执行结果暴露出什么样的缺陷?
上述材料箱位置更新代码的缺陷显露了出来
维护关系完整性的一个简单做法是将关系信息添加到由某个中介对象管理的单个表中。这样,材料箱无需了解机器,机器也无需了解材料箱,中介者对象使用一个表维护材料箱和机器的关系。所有对象只需维护一个到中介者对象的引用。该“表”可能会是Map类的一个实例(从java.util来看)。下图给出了某个类与中介者类的关系图:
Tub对象和Machine对象依赖一个中介者来控制材料箱和机器之间的关系
Tub类有一个位置属性,可记录距该材料箱最近的机器。下面的代码将使用TubMediator对象来管理材料箱与机器之间的关系,以确保该材料箱某个时刻仅在一个位置上:
package com.oozinoz.machine;
public class Tub{
private String id;
private TubMediator mediator;
public Tub(String id,TubMediator mediator)
{
this.id = id;
this.mediator = mediator;
}
public Machine getLocation()
{
return mediator.getMachine(this);
}
public void setLocation(Machine value)
{
mediator.set(this,value);
}
public String toString()
{
return id;
}
public int hashCode()
{
return id.hashCode();
}
public boolean equals(Object obj)
{
if(obj == null) return true;
if(obj.getClass()!=Tub.class)
return false;
Tub that = (Tub)obj;
return id.equals(that.id);
}
}
Tub类的setLocation()方法使用一个中介者来更新材料箱的位置信息,并委派其负责维护与中介者(应该是与机器吧?)之间关系的完整性。Tub类还实现了hashCode()和equals()方法,这样便能够在散列表的恰当位置存储Tub对象。
TubMediator类使用Map对象来存储材料箱和机器之间的关系信息。将这种关系存储在一个表中之后,中介者能够确保对象模型绝对不会允许两台机器拥有同一个材料箱:
public class TubMediator
{
protected Map tubToMachine = new HashMap();
public Machine getMachine(Tub t)
{
return (Machine) tubToMachine.get(t);
}
public Set getTubs(Machine m)
{
Set set = new HashSet();
Iterator i = tubToMachine.entrySet().iterator();
while(i.hasNext())
{
Map.Entry e = (Map.Entry)i.next();
if(e.getValue().equals(m))
set.add(e.getKey());
}
return set;
}
public void set(Tub t,Machine m)
{
tubToMachine.put(t,m);
}
}
如果不是引入了中介者类,只通过在Tub类和Machine类中设置代码逻辑可能无法确保一个材料箱不会同时出现在两个机器中。不管使用何种方法,关系完整性的逻辑属性对于化学材料箱以及机器来说都是收效甚微的。逻辑属性同样也容易出错。一个常见的问题是,更新材料箱和机器时,忘记更新该材料箱原来位置上的机器。
如果将关系管理代码转移到一个中介者对象中,可以让一个独立的类来封装这种对象间交互代码逻辑。使用Mediator模式可以非常容易地确保:在改变Tub对象的位置时,自动将该材料箱从其原来的机器中移走。TubTest.java中的JUnit测试代码描述了这些行为:
public void testLocationChange()
{
TubMediator mediator = new TubMediator();
Tub t = new Tub("T403",mediator);
Machine m1 = new Fuser(1001,mediator);
Machine m2 = new Fuser(1002,mediator);
t.setLocation(m1);
assertTrue(m1.getTubs().contains(t));
assertTrue(!m2.getTubs().contains(t));
t.setLocation(m2);
assertFalse(m1.getTubs().contains(t));
assertTrue(m2.getTubs().contains(t));
}
如果对象模型不涉及关系数据库,那么可以利用Mediator模式来维护该模型的关系完整性。将关系管理逻辑移植到中介者中,可以让这些类专门维护关系完整性。
突破题:与其他模式类似,Mediator模式也是将代码逻辑从一个类移植到另一个类中,请列举出一个从现有类或者类层次中进行重构的其他两种模式。
答:Bridge模式将抽象方法移入一个接口中。
Facade模式可以帮助重构一个大型的应用程序。
Observer模式可以帮助我们重构代码,从而使之遵循MVC架构。
FlyWeight模式将对象中不可变的部分提取出来,从而使之能够被共享。
Builder模式将类中的对象构造逻辑分离出来。
Factory Method模式帮助我们将类层次结构某个方面的行为移入到一个平行的类层次结构中,从而缩小该类层次结构的责任范围。
State模式帮助我们将状态特定的行为移入到一个单独的类中,而Stategy模式帮助我们将策略特定的行为移入到一个单独的类中。
2. 小结:
Mediator模式可降低对象间的耦合程序,避免了相关对象间的显式引用。Mediator模式广泛应用于GUI应用程序的开发过程,借助该模式,可以不必直接管理组件之间错综复杂的交互。Java架构会引导我们实现和使用该模式,比如可以注册对象以监听GUI事件。当我们在利用java开发用户界面的时候,很可能就正在使用Mediator模式。
在构造GUI时,Java会引导我们使用Mediator模式,但它并不要求我们一定要把中介逻辑移出应用程序类。不过,这样做可以简化我们的代码。中介者类可专门用于处理对象间的交互,而应用程序类专门负责创建组件。
引入中介者对象还有其他的优点。例如,我们可以使用中介者对象集中负责维护对象模型的关系完整性。每当需要封装对象间交互方式的时候,我们都可以应用Mediator模式。