Java设计模式--观察者模式【Observer Pattern】

     自二胎政策放开后,越来越多的家庭将拥有两个宝宝。而大部分家庭都希望有儿有女,儿女齐全,这样的家庭无疑是幸福美满的。可是,许多家庭也由于无人照看孩子,而选择放弃生二胎。家庭条件的限制,给爸爸妈妈们留下了终生的遗憾。今天,我们就拿“照看”宝宝的例子,来切入到观察者模式的主题。

  宝宝出生后,身边需要大人照顾。有些家庭只有宝宝的妈妈照顾,而有的家庭爷爷奶奶,外公外婆,七大姑八大姨都加入了照顾宝宝的行列。新生儿刚出生时,基本无法娱乐,照顾宝宝,无非就是看看宝宝饿了没有?是不是想睡觉了?有没有尿尿或者拉粑粑?这里的宝宝就相当于“被观察者”或者主题,而监护人,就承担着“观察者”的角色,一旦观察到宝宝有上述症状,便要改变自己的行为,去让宝宝舒适。

  那么,什么是观察者模式呢?

 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

  观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

      观察者模式的通用类图如下:

     

从上述类图可以看出,观察者模式所涉及的角色有:

  抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。

  具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。

  抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。

  具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

  下面,就拿上述的照顾宝宝的例子,来实现观察者模式。代码如下:

  首先定义抽象主题(Subject)角色,就是被观察者的抽象类或接口。  

package com.pattern.observer.v1;

import java.util.ArrayList;
import java.util.List;
/**
 * 被观察者--抽象角色
 * @author 
 */
public abstract class Baby {
   private String name;//被观察者姓名
   private List<Observer> observers=new ArrayList<Observer>();//观察者列表
   
   public Baby(String name) {
	super();
	this.name = name;
   }
   public String getName(){
		return this.name;
   }
   public void addObserver(Observer observer){//添加观察者
	   this.observers.add(observer);
   }
   public void removeObserver(Observer observer){//移除观察者
	   this.observers.remove(observer);
   }
   protected void notifyAllObservers(Baby baby,String context){//通知所有观察者
	   for(Observer observer:observers){
		   observer.update(baby,context);
	   }
   }
   public abstract void hungry();//饿了
   public abstract void sleepy();//困了
   public abstract void pee();//拉了
}
具体主题角色:新生儿实现类

package com.pattern.observer.v1;
/**
 * 被观察者具体类A
 * @author 
 *
 */
public class NewBaby extends Baby {
	public NewBaby(String name) {
		super(name);
	}

	@Override
	public void hungry() {
		this.notifyAllObservers(this, this.getName()+"宝宝饿了,看看是不是要喂奶...");
	}
	@Override
	public void sleepy() {
		this.notifyAllObservers(this,this.getName()+"宝宝困了,看看是不是要睡觉...");
	}

	@Override
	public void pee() {
		this.notifyAllObservers(this, this.getName()+"宝宝拉了,看看是不是要换尿不湿...");
	}
	
}
抽象观察者,就是观察者的接口

package com.pattern.observer.v1;
/**
 * 观察者总接口
 * @author 
 *
 */
public interface Observer {
   public void update(Baby baby,String context);//更新自己的行为
}
具体观察者,这里以宝宝的妈妈和外婆为例

package com.pattern.observer.v1;
/**
 * 妈妈要照顾宝宝,观察者之一
 * @author 
 *
 */
public class Mother implements Observer {
	@Override
	public void update(Baby baby,String context) {
		if(context.indexOf("饿了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"饿了");
		else if(context.indexOf("困了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"困了");
		else if(context.indexOf("拉了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"拉了");
	    active(context);
	}
    private void active(String context){
    	System.out.println(context);
    	System.out.println("");
    }
    private String getObserverName(){
    	return "妈妈";
    }
}
package com.pattern.observer.v1;
/**
 * 外婆要照顾宝宝,观察者之一
 * @author 
 *
 */
public class GrandMother implements Observer {
	@Override
	public void update(Baby baby,String context) {
	  if(context.indexOf("饿了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"饿了");
	  else if(context.indexOf("困了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"困了");
	  else if(context.indexOf("拉了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"拉了");
	  active(context);
	}
    private void active(String context){
    	System.out.println(context);
    	System.out.println("");
    }
    private String getObserverName(){
    	return "外婆";
    }
}
客户端类:

package com.pattern.observer.v1;
/**
 * 客户端类
 * @author 
 *
 */
public class Client {
	public static void main(String[] args) {
	  //两个观察者 妈妈和外婆
	  Observer o1=new Mother();
	  Observer o2=new GrandMother();
	  //欢欢宝宝有两个观察者
	  Baby huanhuan=new NewBaby("欢欢");
	  huanhuan.addObserver(o1);
	  huanhuan.addObserver(o2);
	  
	  huanhuan.hungry();//欢欢饿了
	  huanhuan.pee();//欢欢拉了
	}

}
结果输出:


 JDK内置观察者模式

 上面是我用java实现了一个简单的观察者模式的例子。实际上我们打开JDK 的帮助文件看看,查找一下Observable 是不是已经有这个类了? JDK 中提供了:java.util.Observable 实现类和java.util.Observer 接口,也就是说我们上面写的那个例子中的Baby抽象类可以改换成java.util.Observale 实现类,将Observer 接口改成java.util.Observer。那么抽象主题角色和抽象观察者角色都不需要自己写了,直接用java中提供的便是。

改写后的代码如下:

具体主题类:

package com.pattern.observer.v2;

import java.util.Observable;
/**
 * 被观察者A:继承java.util.Observable类
 * @author 
 *
 */
public class NewBaby extends Observable {
	private String name;
	public NewBaby(String name) {
		super();
		this.name = name;
	}
	public String getName(){
		return this.name;
	}
	public void hungry() {
		super.setChanged();//告诉观察者:被观察者行为发生改变了
		super.notifyObservers(this.name+"宝宝饿了,看看是不是要喂奶...");//观察者A通知消息给所有观察者
	}
	public void sleepy() {
		super.setChanged();
		super.notifyObservers(this.name+"宝宝困了,看看是不是要睡觉......");
	}
	public void pee() {
		super.setChanged();
		super.notifyObservers(this.name+"宝宝拉了,看看是不是要换尿不湿...");
	}
}
具体观察者:外婆和妈妈

package com.pattern.observer.v2;

import java.util.Observable;
import java.util.Observer;
/**
 * 外婆要照顾宝宝,观察者之一:实现java.util.Observable接口
 * @author 
 *
 */
public class GrandMother implements Observer {
	@Override
	public void update(Observable o, Object obj) {
		NewBaby baby=(NewBaby)o;
		if(obj.toString().indexOf("饿了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"饿了");
		else if(obj.toString().indexOf("困了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"困了");
		else if(obj.toString().indexOf("拉了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"拉了");
		active(obj.toString());
	}
    private void active(String context){
    	System.out.println(context);
    	System.out.println("");
    }
    private String getObserverName(){
    	return "外婆";
    }
	
}
package com.pattern.observer.v2;

import java.util.Observable;
import java.util.Observer;
/**
 * 妈妈要照顾宝宝,观察者之一:实现java.util.Observable接口
 * @author 
 *
 */
public class Mother implements Observer {
	@Override
	public void update(Observable o, Object obj) {
		NewBaby baby=(NewBaby)o;
		if(obj.toString().indexOf("饿了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"饿了");
		else if(obj.toString().indexOf("困了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"困了");
		else if(obj.toString().indexOf("拉了")>-1)System.out.println(getObserverName()+"观察到"+baby.getName()+"拉了");
		active(obj.toString());
	}
    private void active(String context){
    	System.out.println(context);
    	System.out.println("");
    }
    private String getObserverName(){
    	return "妈妈";
    }
}
客户端类:

package com.pattern.observer.v2;

import java.util.Observer;
/**
 * 客户端类
 * @author 
 *
 */
public class Client {

	public static void main(String[] args) {
		  //两个观察者 妈妈和外婆
		  Observer o1=new Mother();
		  Observer o2=new GrandMother();
		  //欢欢宝宝有两个观察者
		  NewBaby huanhuan=new NewBaby("欢欢");
		  huanhuan.addObserver(o1);
		  huanhuan.addObserver(o2);

		  huanhuan.hungry();//欢欢饿了
		  huanhuan.pee();//欢欢拉了
	}

}
结果输出:

观察者模式适用场景:

  1、 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  2、当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
  3、当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

观察者模式的优点:

1、 Subject和Observer之间是松偶合的,分别可以各自独立改变。

2、 Subject在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。

3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。

观察者模式的缺点:

  1、 如果一个Subject被大量Observer订阅的话,在广播通知的时候可能会有效率问题。

观察者模式需要解决的问题:

  1、广播链的问题:如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表A 上写了一个触发器,内容是一个字段更新后更新表B 的一条数据,而表B 上也有个触发器,要更新表C,表C 也有触发器…,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),这还是比较好控制的;

  2、异步处理问题:如果你做过EJB(EnterpriseJavaBean)的开发,这个你绝对不会陌生。EJB2 是个折腾死人不偿命的玩意儿,写个Bean 要实现,还要继承,再加上那一堆的配置文件,小项目还凑活,你要知道用EJB 开发的基本上都不是小项目,到最后是每个项目成员都在骂EJB 这个忽悠人的东西;但是EJB3 是个非常优秀的框架,还是算比较轻量级,写个Bean只要加个Annotaion 就成了,配置文件减少了,而且也引入了依赖注入的概念,虽然只是EJB2 的翻版,但是毕竟还是前进了一步,不知道以后EJB 的路会怎么样。在EJB 中有三个类型的Bean: Session Bean、EntityBean 和MessageDriven Bean,我们这里来说一下MessageDriven Bean(一般简称为MDB),消息驱动Bean,消息的发布者(Provider)发布一个消息,也就是一个消息驱动Bean,通过EJB 容器(一般是Message Queue消息队列)通知订阅者做出回应,从原理上看很简单,就是观察者模式的升级版。这个EJB 是一个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个大家有时间看看Message Queue,就会有更深的了解。


源码下载:http://download.csdn.net/download/pelifymeng2/9994744

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值