设计形式-观察者模式(observer)

设计形式-观察者模式(observer)

设计模式-观察者模式(observer)

设计模式是门很高深的学问,刚开始看的时候感觉就是前辈们总结出的:让代码的的书写更为简便,解偶,以及重用性比较好.

 

现在发现一个设计好的模式,在接口,OO上都是非常的出色.其中的心得,只可意会不可言传.

 

观察者模式

 

定义:定义对象间一对多的依赖,这样一来,当一个对象发生改变,其他依赖者会得到通知并自动更新.

 

举个例子:

 

新京报,他是一个主题,很多人都可以定购他,他不需要了解你订购他是干吗,他只知道你付了钱,他给你一份报纸,到什么地址.

 

如果他修改版面,他会通知你,比如说,最近物价上涨.可能需要上涨几毛钱.(有更改就会通知依赖者)

 

而订购他的人或者公司,属于观察者也就是依赖者,只有2个参数,一个是注册,一个是解除注册.注册后,就会收到报纸的最新消

 

息,一有修改就会通知你.解除注册后,你还是你,但是你已经不属于依赖者了,就不会接到相应的消息.你可以今天注册,今天解

 

除(这是你的事情).不会影响到其他的东西,也就是松耦合.

 

 

当2个对象之间送耦合,它们依然可以相互交互,但是彼此不知道细节.

 

观察者提供了一种设计模式,他让主题和观察者之间松耦合.

 

作为观察者,主题只知道他实现一个接口 Oberver,主题不需要知道他具体是谁,需要实现什么操作.

 

任何时候我们都可以增加新的观察者.因为主题实现的是一个实现oberver的抽象接口列表,所以我们可以随时增加和删除观

 

察者.

 

主题不用了解其他的,只需要给现有注册的观察者发消息就可以了,观察者的增加和改变,不需要修改主题的代码.

 

主题和观察者,我们可以重复使用,改变一方不会印象另外一方.

 

说了好多耦合方面的东西,说到底就是为了达到解耦合的目的.

 

看代码比较清晰

 

我实现了一个电脑厂商和地区代理的,一对多关系依赖.名字起的不好,里面有很多的proxy,可别认为是代理模式.

 

首先实现一个电脑工厂的接口

 

GeneralProxy.java

package com.linpyi.computer;

/**
 * 电脑总代理接口
 * @author work
 *
 */
public interface GeneralProxy {
	/**
	 * 注册电脑代理
	 * @param o
	 */
	public void registerComputerProxy(Observer o);
	/**
	 * 移除电脑代理
	 * @param o
	 */
	public void removeComputerProxy(Observer o);
	/**
	 * 更新代理价格
	 */
	public void notifyComputerProxy();
}

 

他实现了3个接口方法,注册移除和更改.

 

接着写观察者接口,没有多余的方法,就一个updata

 

package com.linpyi.computer;

/**
 * 电脑观察者,自己写的代理接口
 * @author work
 *
 */
public interface Observer {
	/**
	 * 更新电脑信息
	 * @param name 电脑品牌
	 * @param price 电脑价格
	 * @param info 电脑描述
	 */
	public void update(String name,float price,String info);
}

 

还有个代理厂商要实现的接口,也就是一个代理厂商的报价

 

package com.linpyi.computer;
/**
 * 电脑分代理接口
 * @author work
 *
 */
public interface BranchProxy {
	/**
	 * 默认方法
	 */
	public void display();
}

 

 

接着实现具体电脑厂商,我这使用了lenovo,不过名字打错了.

LenvoComputer.java

 

package com.linpyi.computer;

import java.util.ArrayList;

/**
 * lenovo电脑(不好意思这lenovo写错了,暂时不改正了)
 * @author work
 *
 */
public class LenvoComputer implements GeneralProxy {
	
	private ArrayList proxys ;
	private String name;
	private float price;
	private String info;
	
	public LenvoComputer(){
		proxys = new ArrayList();
	}
	
	/**
	 * 更改lenvo分代理的信息
	 */
	public void notifyComputerProxy() {
		// TODO Auto-generated method stub
		for(int i = 0 ;i<proxys.size();i++){
			Observer proxy = (Observer)proxys.get(i);
			proxy.update(name, price, info);
		}
	}
	
	/**
	 * 设置lenvo的电脑价格和信息
	 * @param name 电脑名称
	 * @param price 电脑价格
	 * @param info 电脑描述信息
	 */
	public void setLenvo(String name,float price,String info){
		this.name=name;
		this.price=price;
		this.info=info;
		notifyComputerProxy();
	}

	/**
	 * 注册lenvo代理
	 */
	public void registerComputerProxy(Observer o) {
		// TODO Auto-generated method stub
		proxys.add(o);
	}

	/**
	 * 移除lenvo代理
	 */
	public void removeComputerProxy(Observer o) {
		// TODO Auto-generated method stub
		int i = proxys.indexOf(o);
		if(i>=0){
			proxys.remove(o);
		}
	}

}

 

里面的代码应该不会很难,很容易看的懂的,

 

接着该实现中国区代理 lenovo了,这里要记的实现的是自己写的observer,因为JDK有默认的observer,底下介绍

 

package com.linpyi.computer;

/**
 * 中国地区代理类,使用自己写的代理接口
 * @author 
 *
 */
public class ChinaProxy implements BranchProxy, Observer {
	
	private GeneralProxy generalProxy;
	private String name ;
	private float price;
	private String info;
	
	public ChinaProxy(GeneralProxy generalProxy){
		this.generalProxy=generalProxy;
		generalProxy.registerComputerProxy(this);//注册代理
	}

	/**
	 * 默认方法
	 */
	public void display() {
		// TODO Auto-generated method stub
		System.out.println("中国代理");
		System.out.println("型号"+name);
		System.out.println("价格"+price);
		System.out.println("描述信息"+info);
	}

	/**
	 * 更新方法
	 */
	public void update(String name, float price, String info) {
		// TODO Auto-generated method stub
		this.name=name;
		this.price=price;
		this.info=info;
		display();
	}

}

 

一个代理不够,来2个吧

 

package com.linpyi.computer;

/**
 * 日本代理,使用自己写的代理接口
 * 
 * @author work
 * 
 */
public class JapanProxy implements BranchProxy, Observer {

	private String name;
	private String info;
	private float price;
	private GeneralProxy generalProxy;

	public JapanProxy(GeneralProxy generalProxy) {
		this.generalProxy = generalProxy;
		generalProxy.registerComputerProxy(this);
	}

	public void update(String name, float price, String info) {
		// TODO Auto-generated method stub
		this.name = name;
		this.price = price;
		this.info = info;
		display();
	}

	public void display() {
		// TODO Auto-generated method stub
		System.out.println("日本代理");
		System.out.println("型号" + name);
		System.out.println("价格" + price);
		System.out.println("描述信息" + info);
	}

}

 

看如何使用了

 

package com.linpyi.computer;

public class Client {

	public static void main(String[] args){
		//使用自己写的观察模式方法
		LenvoComputer lenvo = new LenvoComputer();
		ChinaProxy chinaProxy  = new ChinaProxy(lenvo);
		JapanProxy japanProxy  = new JapanProxy(lenvo);
	    lenvo.setLenvo("lenvo0001", 10000, "disk 160G , memory 2G");
	}
}

 看看,我们只需要改变lenovo场商的报价

 

不管是中国还是日本代理,价格也就改变了

 

中国代理
型号lenvo0001
价格10000.0
描述信息disk 160G , memory 2G
日本代理
型号lenvo0001
价格10000.0
描述信息disk 160G , memory 2G

 然后我们需要修改中国代理的其他什么东西,压根就不会影响到厂家,厂家只负责给代理人发报价和基本信息

 

其实代码不是很难,主要是思想,如何能真正的面向接口,面向对象编程,

 

JDK里面有自带的类实现观察者模式,在java.util.Observable,注意这是类而不是接口,类有类的局限性,因为JAVA只能继承

 

一个类,所以如果你的类有继承了实现起来就比较麻烦了.当然局限性不只这一个地方.

 

既然JDK有自己的Obervable,我们就从写一个电脑厂商吧,HP吧,也不错的电脑

 

package com.linpyi.computer;

import java.util.Observable;

/**
 * HP电脑代理,使用的是JDK自带的观察者模式类(Observable)
 * @author work
 *
 */
public class HpComputer extends Observable {

	private String name;
	private float price;
	private String info;
	
	public void measurementsChanged(){
		setChanged();//观察者类里的方法,主要用于开关
		notifyObservers();//继承类里的方法(通知观察者)
	}
	
	public void setMeasurements(String name,float price,String info){
		this.name=name;
		this.price=price;
		this.info=info;
		measurementsChanged();
	}
	
	public String getName(){
		return name;
	}
	
	public float getPrice(){
		return price;
	}
	
	public String getInfo(){
		return info;
	}
}

 

记着这里是实现JDK自带的,别弄混了,里面使用的方法都是已经写好的,只是引用

 

notifyObservers()不说 ,就是通知观察者

 

setChanged()其实就是一个开关

 

setChanged(){
   changed=true;
}

notifyObservers(Object arg){
   if(changed){
      //something
  }
   changed=false;
}

motifyObservers(){
   motifyObservers(null);
}

 

加了一个开关,就会让通知观察者更有弹性,你可以自己设置条件,让他通知观察者,比如几分钟一次,还是什么..这里不做介绍

 

接着写代理地区,中国日本写完了,就写棒子吧

 

package com.linpyi.computer;

import java.util.Observable;
import java.util.Observer;

import com.linpyi.weather.WeatherDataOther;

/**
 * 韩国代理,使用的是JDK自带的接口(Observer)
 * @author work
 *
 */
public class KoreaProxy implements Observer, BranchProxy {
	private Observable observable;
	private String name;
	private String info;
	private float price;

	public KoreaProxy(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);
	}

	public void update(Observable o, Object arg) {
		// TODO Auto-generated method stub
		if (o instanceof HpComputer) {
			HpComputer computer = (HpComputer) o;
			this.name = computer.getName();
			this.price = computer.getPrice();
			this.info = computer.getInfo();
			display();
		}
	}

	public void display() {
		// TODO Auto-generated method stub
		System.out.println("韩国代理");
		System.out.println("型号" + name);
		System.out.println("价格" + price);
		System.out.println("描述信息" + info);
	}

}

 

这里实现的Observer接口也是JDK自带的

 

  加个印度的.

 

package com.linpyi.computer;

import java.util.Observable;
import java.util.Observer;

/**
 * 印度电脑代理(使用JDK自带的接口)
 * @author work
 *
 */
public class IndiaProxy implements Observer, BranchProxy {
	
	private Observable observable;
	private String name;
	private String info;
	private float price;
	
	public IndiaProxy(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);//增加观察者(JDK自带的)
	}

	/**
	 * 更新方法,使用的是JDK自带的方法
	 */
	public void update(Observable o, Object arg) {
		// TODO Auto-generated method stub
		if (o instanceof HpComputer) {
			HpComputer computer = (HpComputer) o;
			this.name = computer.getName();
			this.price = computer.getPrice();
			this.info = computer.getInfo();
			display();
		}
	}

	public void display() {
		// TODO Auto-generated method stub
		System.out.println("印度代理");
		System.out.println("型号" + name);
		System.out.println("价格" + price);
		System.out.println("描述信息" + info);
	}

}

 

OK,完成了,

 

测试一下吧

 

package com.linpyi.computer;

public class Client {

	public static void main(String[] args){
		//使用自己写的观察模式方法
		LenvoComputer lenvo = new LenvoComputer();
		ChinaProxy chinaProxy  = new ChinaProxy(lenvo);
		JapanProxy japanProxy  = new JapanProxy(lenvo);
	    lenvo.setLenvo("lenvo0001", 10000, "disk 160G , memory 2G");
	    //使用JDK的观察模式方法
	    HpComputer hp = new HpComputer();
	    KoreaProxy koreaProxy = new KoreaProxy(hp);
	    IndiaProxy IndiaProxy = new IndiaProxy(hp);
	    hp.setMeasurements("hp0001", 12000, "disk 250G , memory 2G");
	}
}

 

 

运行结果

 

中国代理
型号lenvo0001
价格10000.0
描述信息disk 160G , memory 2G
日本代理
型号lenvo0001
价格10000.0
描述信息disk 160G , memory 2G
印度代理
型号hp0001
价格12000.0
描述信息disk 250G , memory 2G
韩国代理
型号hp0001
价格12000.0
描述信息disk 250G , memory 2G

 

达到的效果一样吧,

 

看仔细了,真的一样吗

 

其实看看2个运行的顺序,其实是相反的.难道有错,其实谁都没错,只是计算的方法不一样.

 

但是如果我们依赖了他们的顺序,就是我们错了,错误的顺序导致错误的结果.

 

以下引用

 

java.util.Observable的黑暗面

如同你发现的.可观察者是一个类而不是一个接口,更糟糕的是,他甚至没有实现一个接口.不幸的是,java.util.Observable的实现有许多的问题,限制了她的使用和复用.这并不是说他没提供有用的功能,我们只是提醒大家注意一些事实.

Observable 是一个类

首先,因为Observable是一个类.你必须设计一个类继承他,如果某类想同时具有Observable类和另外一个超类的行为,你就会陷入两难,JAVA不支持多重继承.这限制了Observable的复用能力.
再者,因为没有Observable接口,所有无法建立自己的实现.和JAVA内置的ObserverAPI搭配使用,也无法将java.util的实现换成另外一套做法.

Observable将关键的方法保护起来

看了ObservableAPI,你会发现setChanged()方法被保护起来,意味着,除非你继承自Observable,否则你无法创建Observable实例并组合到你自己的对象里来.这个设计违反了第二个设计原则:"多用组合,少用继承"

 

 

其实观察者模式差不多已经了解了,主要的写法就这些.

 

当然不在乎怎么写,在乎你怎么想,主要还是思维上要有面向接口和面向对象的思维,面向接口是门很深的学问

 

介绍一本设计模式的书<Head.First.设计模式.中文版>

 

很想发到论坛上,结果发现好象没发成功,郁闷,算 了

1 楼 jonyzhu 2008-08-25  
实际应用中,考虑网络状况和数据库设计(比如,你的中国、日本代理,肯定是一套表里面的2条记录,用一个“代理”类的对象带着不同的id参数取出、更新),几乎不可能这样来实现对不同市场产品的同步更新。
单机的应用软件可能还可以用上。
设计模式不是设计的,是总结的。
2 楼 linpyi 2008-08-25  
实际中也不可能是这样修改价格的,我只是用这个来打个比方,可以比较清楚的了解观察者模式的意思.设计模式是前辈的总结,对我们这些后生来说,是一个比较好的开始,当然要有一定的经验,才能理解里面的好处.我在这只将思路,不涉及到具体的实现方式.设计形式-观察者模式(observer)
3 楼 jonyzhu 2008-08-25  
了解,我第一次看到《设计模式》也是这样的心情设计形式-观察者模式(observer)
但最后,我终于明白:实际工作里面的设计模式,还是要靠我们自己来发现。
4 楼 east_java 2008-08-26  
设计形式-观察者模式(observer)
简单易懂.