上一篇:界面组装器模式

下一篇:State模式在客户端软件中的应用

Single Call 模式

在进行面向对象得分析与设计的实践过程中我们经常会遇到聚合(Aggregations)与组合(Composition)这两种关系,聚合是一种关联形式,它指明一个聚集(整体)和组成部分之间的整体与部分的关系。组合是指一种带有很强主从关系,成分的生命期一致的聚集关联形式。一个部分可以仅属于一个组成。没有固定多重性的部分可以在组成创建后再被创建。但是一旦被创建,这些部分将与组成同时存在并同时消亡(共享生存周期)。这些部分也可以在组成消失前被移开。组成可以是递归的。

一个典型的聚合的例子是计算机,计算机的个部分和主机是松散的从属关系,一台计算机的外设可以随时接到另一台主机上使用,也就是说计算机的主机和外设有各自的生存周期,例如如果主机发生了故障,我们仍然可以把显示器接到另一台计算机上使用。

典型组合关系的例子是桌子,下图表示的就是桌子类(Coffee Table),它包括桌面类(Table Top)和桌腿类(Leg),桌面和桌腿只能从属于桌子而不会从属于其他,当桌子不存在时,桌面和桌腿也就没有意义了,也就是说三个类处于一个生存周期。

我们来考虑下面的例子,比如我们现在需要一个汽车类(Car)和一个发动机类(Engine),这两个类是个典型的组合组合关系,也就是说发动机类能在汽车类的里面被实例化而且只能在汽车类被实例化。可是我们在开发的过程中如何实现组合关系呢?

首先,我们必须保证Engine类不能直接被实例化,为了达到这个目的我们必须屏蔽掉Engine类的构造函数,例如:

这样我们就不能直接用new Engine()的方法来创建实例,而只能通过Engine类的一个静态方法来得到Engine类的实例,例如:

这种方法和我们实现singleton的方法是一模一样的。现在我们的Engine类已经不能直接被实例化了,但是还不能保证它只能被Car类实例化,因为在任何类中我们都可以用Engine engine=Engine.getobject()的方法得到Engine类的实例。为了保证Engine类是被Car类实例化的我们可以在Car类调用Engine.getobject()时把自己本身的引用作为一个参数传到Engine类中,例如Engine engine=Engine.getobject(this),这时的Engine类必须要做一点修改

这样只有在Car类中执行Engine engine=Engine.getobject(this)时,才能真正返回一个Engine类的实例。否则会收到一个类型不匹配的异常。

我们可能觉得到现在应该大功告成了,原来实现这么简单。

可是,细心的人会发现这样的设计存在一个很大的漏洞,Engine类虽然我们屏蔽掉了它的构造函数,但是Car类的构造函数我们并没有屏蔽掉,在任何类中如果我们执行

我们仍然可以得到一个Engine类的实例,这样的结果是我们不能接受的,所以我们还要做一些改动。

我们看到在Car类中增加了一个方法--can_load(),用这个方法能我们能判断出Engine.getobject方法是不是从Car类内部调用的,如果是我们就返回一个Engine类的实例,否则我们就返回一个空指针null,相应的Car类的代码如下。

这时的Car类,多了一个布尔类型的一个变量can_load,它的作用是只有can_load的值为真时Engine类的getobjec方法才能返回一个Engine类的实例。而can_load是私有类型的变量,初始值为假,只有在Car类的内部才能改变can_load的值。这样我们在使用Engine.getobject(this)时首先把can_load的值设为真,在返回Engine类的一个实例以后再把can_load的值设为假,这样我们就可以很好的控制Engine类只能在Car类内部实例化。

最后一点要说明的是Car类的can_load()方法必须为final类型的,因为如果是非final类型的方法的话我们可以派生Car类,然后重载can_load()方法,例如:

这样我们就可以在任何类中用Engine engine=Engine.getobject(new newCar())得到Engine类的实例,这也是我们不想看到的结果,所以一定要把Car类的can_load()方法设为final类型,这样才能保证Car类的can_load()方法不会被派生类重载。

完整的源代码:

上一篇:界面组装器模式

下一篇:State模式在客户端软件中的应用