`

锈才学设计模式之 —— 观察者模式(Observer Pattern)

阅读更多

锈才学设计模式之  —— 观察者模式 

观察者模式:定义对象的一对多的关系,这样当主题对象改变状态时,其它的观察者对象都会收到通知,自动更新.

 

说明:

       在真实世界中存在很多类似的模型,比如:订报纸,找中介

       订报纸: 你在报社订阅报纸后,你就不用关心怎么获取信息,只要报社有新的信息报纸就会派送给你.

       找中介: 你在中介登记后,你就不用关心怎么找到新房子,中介如果有房源的信息,就会联系你去看房.

这两种都是曲型的对象一对多关系,报社可以有很多订阅者.中介可以有很多登记者.他们就是主题提供者(被观察者),而订阅者和登录者都是观察者.他们依赖于主题,只要主题变化就会被通知.如图:

报社用例

在这里我举一个购物商城商品库存提醒的例子.假如商城的某个商品库存不足时,我们希望能够自动通知展柜把该商品下架,同时自动通知商家补货,我们将该业务的模型图列出:

商品库存通知

示例代码:
    == 商品库存提醒类 == 

package com.jody.pattern.observer;

public class Merchandise {
	private long stock;//库存
	private String id;//商品编号
	/**
	 * 盘点商品库存
	 */
	public void checkStock(){		
		ShowBox.update(id, stock);//展柜下架
		Merchant.update(id, stock);//商家补货		
	}	
}

 

 在这个商品类中,盘点商品库存时,就去将展柜该商品下架,商家补货.这种做法可行,但是有违OO的概念和原则:

  1.应该是面向接口编程,而非面向实现编程.

  2.封装改变的地方,成为单独的类,尽量复用.

  3.各个对象之间尽量低耦合.

按照上面的代码,展柜下架、商家补货都没有独立成单独的类,如果增加一个功能:当商品库存不足时就生成该商品的购买报表,方便数据挖掘小组参考.这时就必须修改商品类的代码以满足功能.这样的话就有局限性,不够弹性.而且展柜下架和商家补货都提供了更新的方法(update),这像是一个统一的接口,应该把它抽象出来。

这时最好就用到观察者模式,重新修改模型如下图:

 

通过使用观察者模式后,对象之间都是松耦合的,主题只知道观察者实现了Observer接口,并不需要知道观察者是什么,要做什么事。任何时候都可以增加新的观察者,因为主题依赖的是一个实现Observer接口的观察者列表,所以可以随时增加观察者,主题不会有任何影响,不需要修改代码,只需要实现Observer接口并注册成观察者。主题状态改变时就会通知他。

设计原则:尽量将交互对象设计成松耦合

 

 示例代码:
    == 主题接口 ==  

package com.jody.pattern.observer;

public interface Subject {
	//注册成观察者
	public void registerObserver(Observer o);
	//注销观察者
	public void removeObserver(Observer o);
	//通知观察者
	public void notifyObserver();
}

  

   == 商品类 ==   

package com.jody.pattern.observer;

import java.util.ArrayList;
import java.util.List;

public class Merchandise implements Subject{
	private List<Observer> observers;
	private long stock;//库存
	private String id;//商品编号
	
	//构造器建立观察者列表
	public Merchandise(){
		observers = new ArrayList<Observer>();
	}
	//盘点商品库存	 
	public void checkStock(){	
		notifyObserver();
	}
	public void notifyObserver() {
		//通知每一个观察者
		for(Observer o:observers){
                                                //o.update(object o)//送数据
			o.update(Merchandise s,stock);//此处把主题传过去,从主题拉数据
		}		
	}
	public void registerObserver(Observer o) {
		//注册观察者
		observers.add(o);		
	}
	public void removeObserver(Observer o) {
		//注销观察者
		int i = observers.indexOf(o);
		if(i>=0){
			observers.remove(i);
		}
	}	
 	public long getStock() {
 		 return stock;
 	}
 	public void setStock(long stock) {
 		 this.stock = stock;
 	}
 	public String getId() {
  		return id;
 	}
 	public void setId(String id) {
  		this.id = id;
 	} 
}

 

  == Observer接口 ==   

package com.jody.pattern.observer;

public interface Observer {
	//更新
	// public void update(String id,long stock);//接受主提送的数据
	public void update(Merchandise s,long stock);//从主题拉数据
}

 

   == 商家观察者 ==

package com.jody.pattern.observer;

public class MerchantObserver implements Observer {
	private Subject subject;
	MerchantObserver(Subject s){
		//构造器中注册自身成观察者
		this.subject = s;
		subject.registerObserver(this);
	}
	/*
	//主题送数据
	public void update(String id, long stock) {
		// 商家补货		
	}*/
	//从主题处拉数据
	public void update(Merchandise s, long stock) {
		// 商家补货	
		s.getId();
		s.getStock();	
	}
}

 

   ==展柜观察者 ==

package com.jody.pattern.observer;

public class ShowBoxObserver implements Observer{
	private Subject subject;
	ShowBoxObserver(Subject s){
		//构造器中注册自身成观察者
		this.subject = s;
		subject.registerObserver(this);
	}
	/*
	//主题送数据
	public void update(String id, long stock) {
		// 商家补货		
	}*/
	//从主题处拉数据
	public void update(Merchandise s, long stock) {
		// 商家补货	
		s.getId();
		s.getStock();	
	}
}

 

   ==测试类 ==

package com.jody.pattern.observer;

public class Test {
	public static void main(String[] args){
		//创建一个商品主题类
		Merchandise subject = new Merchandise();
		//将主题类传给观察者,供观察者注册
		Observer merchant = new MerchantObserver(subject);
		Observer showbox = new ShowBoxObserver(subject);
		//盘点库存,如果库存不足,则通知所有观察者
		subject.checkStock();
	}
}

 

 在商品主题类中可以选择主题送数据通知观察者,也可以提供getter方法,供观察者从主题拉数据,有时观察者不需要所有数据,如果将全部数据通知所有观察者,有点不合规则,所以提供两种方式:送数据、拉数据。

 

在JDK AIP中,已经实现了观察者模式。可以查看JDK查看内置的观察者模式代码:java.util.Observer。在内置的观察者模式中,有一个setChanged()的标记状态,当状态为true时,即通知所有观察者,为falsh不通知。有时候不需要每时每刻都要通知观察者,所以在通知前先设置标记状态,再通知,就可以进行控制。具体的大家可以查看源代码,基本思想是一样的。

  • 大小: 13.9 KB
  • 大小: 13.7 KB
  • 大小: 51.8 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics