Single Call 模式

Comments

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

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

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

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

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

public class Engine {
  private Engine() {  }
     }

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

public class Engine {
  private Engine() {  }
  static Engine  getobject()
{
  new Engine();
} 
    }

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

public class Engine {
  private Engine() {  }
  static Engine  getobject(Car o)
{
  new Engine();
} 
    }

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

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

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

Engine engine=Engine.getobject(new Car())

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

//Engine.java
public class Engine {
  private Engine() {  }
  static Engine  getobject(Car o)
  {
      if (o.can_load() )
      {
             return( new Engine());
      }
    else
      return null;
  }
  
}

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

//Car.java
public class Car {
  private Engine engine;
  private boolean can_load=false;
  public Car()
  {
      engine=  get_engine();
  }
final  public boolean can_load()
  {
    return can_load;
  }
  private Engine get_engine()
  {
       Engine my_engine;
       can_load=true;
       my_engine=Engine.getobject(this) ;
       can_load=false;
       return my_engine;
  }
 }

这时的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()方法,例如:

public class newCar extends Car {
  public newCar() {
  }
  public boolean can_load(){return true;}
  
}

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

完整的源代码:

//Engine.java
  public class Engine {
  private Engine() {  }
  static Engine  getobject(Car o)
  {
      if (o.can_load() )
      {
             return( new Engine());
      }
    else
      return null;
  }
  public void start()
  {
  System.out.println("engine started");
  }
  public void stop()
  {
  System.out.println("engine stopped");
  }
}
  
  //Car.java
   public class Car {
 private Engine engine;
   private boolean can_load=false;
  public Car()
  {
      engine=  get_engine();
  }
final  public boolean can_load()
  {
    return can_load;
  }
  private Engine get_engine()
  {
       Engine my_engine;
       can_load=true;
       my_engine=Engine.getobject(this) ;
       can_load=false;
       return my_engine;
  }
  public void start()
  {
    if (engine!=null)
       engine.start();
  }
  public void stop()
  {
    if(engine!=null)
       engine.stop();
  }
  public static void main(String[] args) {
     Car car =new Car();
     car.start() ;
     car.stop() ;
  }
}

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=53156
ArticleTitle=Single Call 模式
publish-date=04212003