螳螂捕蝉、黄雀在后——从一个成语谈观察家模式

  观察家模式是一个事件通知模式,被观察者发生某个事件,或者状态发生某个变化,就通知观察者,这样观察者就能采取适当的行动。下面我以一个简单的例子来说明一下这个模式的应用。

我们都知道,蜜蜂是勤劳的精灵,它总是四处采蜜。只要花朵的花瓣一张开,她就飞上去采蜜。我们轻易就能想到,在这里,蜜蜂应该是一个观察者,而花朵是一个被观察者。只要花朵发生花瓣张开事件,就通知了观察者蜜蜂,蜜蜂就可以去采蜜了。

现在我们就来用java程序模拟蜜蜂采蜜。

JavaAPI为我们设计好了这个模式,我们的被观察者需要继承Observable类,而观察者需要实现Observer接口。

现在我们来看看实际的代码:

package observer;

 

import java.util.Observable;

import java.util.Observer;

 

public class Flower extends Observable

{

      public void open()          -----------------------------------------------1

      {

               System.out.println("The flower is opening!");   -----------2

               this.setChanged();        ----------------------------------------3

               this.notifyObservers();   ---------------------------------------4

      }

      public void registObserver(Observer observer)   -------------------5

      {

               this.addObserver(observer);            --------------------------6

      }

 

      public static void main(String[] args)

      {

      }

}

 

标号1行定义了一个方法,在这个方法里,主要要做三件事:第一被观察者的动作,见标号2行的代码;第二设置被观察者的状态变化,如标号3行的代码;第三通知观察者,如标号4行的代码。

package observer;

 

import java.util.Observable;

import java.util.Observer;

 

public class Bee implements Observer

{

 

      public void update(Observable arg0, Object arg1)

      {

               // TODO Auto-generated method stub

               System.out.println("It is bee's meat time!");

 

      }

      public static void main(String[] args)

      {

      }

}

观察者的实现比较简单,主要就是要实现update方法,在这个方法里实现观察者在观察到被观察者动作后所要做的动作。

下面是测试代码:

Bee bee = new Bee();

               Flower flower = new Flower();

               flower.registObserver(bee);

               flower.open();

测试结果如下:

The flower is opening!

It is bee's meat time!

现在我们已经基本熟悉了观察家模式的用法,可以用它来设计我们一些日常所见的现象。如我们常说的一句成语是“螳螂捕蝉,黄雀在后”就可以利用我们的观察家模式来进行设计。螳螂在四处搜寻着蝉,如果蝉趴在树枝上一动不动,那么螳螂是很难发现蝉。但如果蝉一有动作,就会被螳螂发现;螳螂一旦发现蝉,就会对蝉发起攻击;但螳螂万万没有想到,黄雀也在后面搜寻着螳螂,一旦螳螂有所动作,黄雀也会对螳螂发动攻击。

很明显,蝉是一个被观察者,而黄雀是一个观察者,螳螂则对蝉来说是一个观察者,对黄雀来说是一个被观察者,所以螳螂既是观察者又是被观察者。

代码如下:

package observer;

 

import java.util.Observable;

import java.util.Observer;

 

public class Cicada extends Observable

{

  public void move()

  {

      System.out.println("The cicada is moving....");

      this.setChanged();

      this.notifyObservers();

  }

  public void registObserver(Observer observer)

  {

      this.addObserver(observer);

  }

 

  public static void main(String[] args)

  {

  }

}

 

 

package observer;

 

import java.util.Observable;

import java.util.Observer;

 

 

public class DevilHorse extends Observable implements Observer

{

  public void update(Observable arg0, Object arg1)

  {

      // TODO Auto-generated method stub

      System.out.println("It is time for Devil horse to attack....");

      this.setChanged();

      this.notifyObservers();

 

  }

  public void registObserver(Observer observer)

  {

      this.addObserver(observer);

  }

  public static void main(String[] args)

  {

  }

}

 

 

package observer;

 

import java.util.Observable;

import java.util.Observer;

 

public class YellowBird implements Observer

{

  public void update(Observable arg0, Object arg1)

  {

      // TODO Auto-generated method stub

      System.out.println("It is time for a yellow bird to attrack....");

 

  }

 

  public static void main(String[] args)

  {

      

  }

}

 

在上面的代码中,类DevilHorse既是观察者,又是被观察者,所以它既继承了Observable类,又实现了Observer接口。

下面是测试代码:

Cicada cicada = new Cicada();

      DevilHorse devilHorse = new DevilHorse();

      YellowBird yellowBird = new YellowBird();

      cicada.registObserver(devilHorse);

      devilHorse.registObserver(yellowBird);

      cicada.move();

运行结果:

The cicada is moving....

It is time for Devil horse to attack....

  It is time for a yellow bird to attrack....

 

到了上面为止,我们已经把观察家模式全面的剖析了一遍。现在来对该模式作一些深入的研究。

我们还是以蜜蜂采花为例,现在我们加入鸟儿吃蜂这一个案例。很明显,我们的Flower类不用变,而Bee类则既是观察者又是被观察者,需要做改动为如下代码:

package observer;

 

import java.util.Observable;

import java.util.Observer;

 

public class Bee extends Observable implements Observer

{

  public void update(Observable arg0, Object arg1)

  {

      // TODO Auto-generated method stub

      System.out.println("It is bee's meat time!");

      this.setChanged();

      this.notifyObservers();

 

  }

  public void registObserver(Observer observer)

  {

      this.addObserver(observer);

  }

 

  public static void main(String[] args)

  {

  }

}

Bird类为:

package observer;

 

import java.util.Observable;

import java.util.Observer;

 

public class Bird implements Observer

{

 

  public void update(Observable arg0, Object arg1)

  {

      // TODO Auto-generated method stub

      System.out.println("It is a bird's meat time....");

 

  }

 

  public static void main(String[] args)

  {

  }

}

 

测试代码:

Bee bee = new Bee();

      Bird bird = new Bird();

      Flower flower = new Flower();

      flower.registObserver(bee);

      bee.registObserver(bird);

      flower.open();

测试结果:

The flower is opening!

It is bee's meat time!

  It is a bird's meat time....

 

我们看看上面的被观察者类,方法:

public void registObserver(Observer observer)

  {

      this.addObserver(observer);

  }

每次都被原封不动的照抄下来;而在被观察者的动作方法里头,总有

this.setChanged();

      this.notifyObservers();

这两句是必须照抄的。

每一个被观察者都必须这样,很明显,这是冗余代码,我们需要想办法解决。对于这样的冗余代码,我们可以轻松的想到用模板方法模式来解决。

还有,我们来看测试代码,我们对类的初始化都有很明显的局限性。如:

Bee bee = new Bee();

      Bird bird = new Bird();

      Flower flower = new Flower();

flower.registObserver(bee);

我们来看flowerbee的依赖关系,我们都知道依赖颠倒原则说的是要依赖抽象而不要依赖具体实现,而flowerbee的依赖明显是依赖具体实现bee,不满足依赖颠倒原则。这带来的缺点也是显而易见的:如果有一个蝴蝶类也在观察着Flower类,那么在运行期才能知道Flower类的观察者,该怎么办?

以上的两个缺点需要我们对观察家模式作进一步的包装。

我们首先对被观察者作包装,代码如下:

package observer;

 

import java.util.Observable;

import java.util.Observer;

 

public abstract class BaseObservable extends Observable

{

  protected void baseDo()

  {

      observableDo();

      this.setChanged();

      this.notifyObservers();

  }

  public void registObserver(Observer observer)

  {

      this.addObserver(observer);

  }

  public abstract void observableDo();

}

BaseObservable类是一个模板方法模式的一个直接应用。我们的被观察者只要继承了这个类,就只需实现observableDo即可,在该方法里只写被观察者的动作,而无须关注其他。

然后是对既是观察者又是被观察者的类进行包装:

package observer;

 

import java.util.Observable;

import java.util.Observer;

 

public abstract class BaseObservableObserver extends Observable implements Observer

{

  public void update(Observable arg0, Object arg1)

  {

      // TODO Auto-generated method stub

      observableObserverDo(arg0,arg1);

      this.setChanged();

      this.notifyObservers();

 

  }

  public void registObserver(Observer observer)

  {

      this.addObserver(observer);

  }

  public abstract void observableObserverDo(Observable arg0, Object arg1);

 

  public static void main(String[] args)

  {

  }

}

同样,我们的观察和被观察者只要继承了BaseObservableObserver类,就只要实现observableObserverDo方法即可,在这个方法里头实现它的动作。

最后是对观察者的包装:

package observer;

 

import java.util.Observable;

import java.util.Observer;

public abstract class BaseObserver implements Observer

{

  public void update(Observable arg0, Object arg1)

  {

      ObserverDo(arg0,arg1);

  }

  public abstract void ObserverDo(Observable arg0, Object arg1);

  public static void main(String[] args)

  {

  }

}

同样,观察者只要继承了BaseObserver类,就只要在ObserverDo方法里实现观察家的动作就好了。

下面我们来重新设计FlowerBeeBird类:

package observer;

 

public class Flower extends BaseObservable

{

  public void observableDo()

  {

      System.out.println("The flower is opening...");

  }

}

 

package observer;

 

import java.util.Observable;

 

public class Bee extends BaseObservableObserver

{

  public void observableObserverDo(Observable arg0, Object arg1)

  {

      System.out.println("It is a bee's meal time...");

  }

}

 

package observer;

 

import java.util.Observable;

 

public class Bird extends BaseObserver

{

  public void ObserverDo(Observable arg0, Object arg1)

  {

      System.out.println("aha,it's a bird's meal time...");

  }

}

 

现在我们可以看到这三个类简洁了很多,只有和业务相关的动作方法,来看我们的测试类:

BaseObservable flower = new Flower();

      BaseObservableObserver bee = new Bee();

      BaseObserver bird = new Bird();

      flower.registObserver(bee);

      bee.registObserver(bird);

      flower.baseDo();

测试结果:

The flower is opening...

It is a bee's meal time...

  aha,it's a bird's meal time...

 

我们可以看到,flowerbee的依赖关系也由具体类Bee变成了抽象类BaseObservableObserver。有利于我们的系统扩展。

如,我们增加了一个Butterfly类:

package observer;

 

import java.util.Observable;

 

public class Butterfly extends BaseObservableObserver

{

  public void observableObserverDo(Observable arg0, Object arg1)

  {

      System.out.println("The butterfly is coming for a meal...");

  }

}

 

我们的测试端就可以有这样的代码:

BaseObservable flower = new Flower();

      BaseObservableObserver bee = new Bee();

      BaseObservableObserver butterfly = new Butterfly();

      BaseObserver bird = new Bird();

      ArrayList list = new ArrayList();

      list.add(bee);

      list.add(butterfly);

      for(int i=0;i<list.size();i++)

      {

           BaseObservableObserver boo = (BaseObservableObserver)list.get(i);

           flower.registObserver(boo);

           boo.registObserver(bird);

      }

      flower.baseDo();

这样使得观察家模式更加灵活和易于扩展。

测试结果:

The flower is opening...

The butterfly is coming for a meal...

aha,it's a bird's meal time...

It is a bee's meal time...

  aha,it's a bird's meal time...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值