你的位置:首页 > ASP.net教程

[ASP.net教程]设计模式(二):观察者模式


一、概述

  观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式有时成为发布/订阅模式,就是让多个对象在一个对象的状态改变时被通知到。

二、解决问题

  当一个系统有多个类协同工作,如果在一个类中需要知道另外一个类的实现细节才能让系统运转,就会导致系统耦合过紧,以后相互依赖的类改变了或者新增了依赖的类,很多类需要同时更改。为了让交互对象之间能够松耦合,我们可以使用观察者模式。

三、结构类图

 

四、成员角色

  抽象主题(Subject):把所有对观察者对象的引用保存到一个集合中,每个抽象主题角色都有任何数量的观察者。抽象主题角色提供一个接口,可以增加和删除观察者。一般用一个抽象类和接口实现。

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

  具体主题(ConcreteSubject):继承抽象主题,在具体主题内部状态改变时,给所有登记过的观察者发出通知。

  具体观察者(ConcreteObserver):实现抽象观察者所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者可以保存一个指向具体主题的引用。

五、应用实例

  下面是银行账号资金变动下发通知的例子,只要账号资金变动,就会同时下发app客户端消息、邮件消息、短信消息通知客户账号资金变动了

  //主题接口

public interface Subject {	public void registerObserver(Observer o);	public void removeObserver(Observer o);	public void notifyObservers();}

  //抽象主题

abstract public class AbstractSubject implements Subject{	//可以在这里新增Subject没有的方法,以便子类共用}

  //观察者接口

public interface Observer {	public void update(double money);}

  //app客户端观察者

public class APPObserver implements Observer{	public void update(double money){		//发送app客户端通知账号资金变动		System.out.println("app客户端提醒:您的账户资金变动:" + money);	}}

  //邮件观察者

public class MailObserver implements Observer{	public void update(double money){		//发送邮件通知账号资金变动		System.out.println("邮件提醒:您的账户资金变动:" + money);	}}

  //短信观察者

public class SmsObserver implements Observer{	public void update(double money){		//发送短信通知账号资金变动		System.out.println("短信提醒:您的账户资金变动:" + money);	}}

  //客户银行账号主题

public class AccountSubject extends AbstractSubject{	//观察者集合	private List<Observer> observers;	//资金变动	private double money;		public AccountSubject(){		//主题实例化时,初始化观察者集合		observers = new ArrayList<Observer>();	}		//账号资金变动时通知观察者	public void notifyObservers() {		if(observers.size() > 0){			for(Observer observer:observers){				observer.update(money);			}		}			}	//注册观察者	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 void changeAccount(double money){				System.out.println("账户资金变动:" + money);		this.money = money;				//通知观察者		notifyObservers();	}}

  //测试观察者

public class TestObserver {	public static void main(String[] args){		//创建账号主题		AccountSubject subject = new AccountSubject();				//创建观察者		Observer appObserver = new APPObserver();		Observer smsObserver = new SmsObserver();		Observer mailObserver = new MailObserver();				//注册观察者到账号主题		subject.registerObserver(appObserver);		subject.registerObserver(mailObserver);		subject.registerObserver(smsObserver);					subject.changeAccount(7.8);	}}

  

测试结果:

其实 Java内置了观察者模式,我们可以直接使用java.util包中的Obserber接口和Observable类实现观察者模式。下面我们把上面的代码稍微修改,用Java内置的观察者模式实现银行账号资金变动下发各种通知。

  //首先我们创建被观察者

import java.util.Observable;public class AccountSubject extends Observable{		//资金变动数额	private double money;		public AccountSubject(){	}		//改变账户资金	public void changeAccount(double money){				System.out.println("(测试java自带观察者模式)账户资金变动:" + money);		this.money = money;				//被观察者状态改变		this.setChanged(); 		//java内部实现推拉通知,使用时直接调用notifyObservers(),		//或者notifyObservers(Object o),传递的参数会在观察者类接收到		notifyObservers();	}	public double getMoney() {		return money;	}	public void setMoney(double money) {		this.money = money;	}	}

  //app客户端观察者

import java.util.Observable;import java.util.Observer;public class APPObserver implements Observer{	//在观察者类图中我们看到,观察者可以持用被观察者的引用	Observable observale;		public APPObserver(Observable observable){		this.observale = observable;		//直接调用java自带的方法,把观察者注册到被观察者		observable.addObserver(this);	}	//具体观察者实现Observer接口的方法,	//arg1参数就是被观察者的notifyObservers(Object o)传过来的参数	public void update(Observable arg0, Object arg1) {		if(arg0 instanceof AccountSubject){			AccountSubject accountSubject = (AccountSubject)arg0;			//发送app客户端通知账号资金变动			System.out.println("app客户端提醒:您的账户资金变动:" + accountSubject.getMoney());		}			}}

  //邮件观察者

import java.util.Observable;import java.util.Observer;public class MailObserver implements Observer{		//在观察者类图中我们看到,观察者可以持用被观察者的引用	Observable observale;		public MailObserver(Observable observable){		this.observale = observable;		//直接调用java自带的方法,把观察者注册到被观察者		observable.addObserver(this);	}		//java的观察者有自带的update方法	public void update(Observable arg0, Object arg1) {		if(arg0 instanceof AccountSubject){			AccountSubject accountSubject = (AccountSubject)arg0;			//发送app客户端通知账号资金变动			System.out.println("邮件提醒:您的账户资金变动:" + + accountSubject.getMoney());		}	}}

  //短信观察者

import java.util.Observable;import java.util.Observer;public class SmsObserver implements Observer{		//在观察者类图中我们看到,观察者可以持用被观察者的引用	Observable observale;		public SmsObserver(Observable observable){		this.observale = observable;		//直接调用java自带的方法,把观察者注册到被观察者		observable.addObserver(this);	}	//java的观察者有自带的update方法	public void update(Observable arg0, Object arg1) {		if(arg0 instanceof AccountSubject){			AccountSubject accountSubject = (AccountSubject)arg0;			//发送app客户端通知账号资金变动			System.out.println("短信提醒:您的账户资金变动:" + + accountSubject.getMoney());			}				}}

  //测试java自带观察者模式

import java.util.Observer;public class TestObserver {	public static void main(String[] args){		//创建被观察者,也就是账号主题		AccountSubject observble = new AccountSubject();						//创建观察者		Observer appObserver = new APPObserver(observble);		Observer smsObserver = new SmsObserver(observble);		Observer mailObserver = new MailObserver(observble);				//账号资金变动		observble.changeAccount(7.8);	}}

  测试结果:

 

六、优点与缺点

  1.优点

  (1)、观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖抽象,而不是依赖具体。从而使各自的变化都不会影响到另一方。主题并不知道任何一个具体的观察者,只知道他们有一个共同的接口。

  (2)、观察者模式支持“广播通信”。主题会向所有的观察者发出通知。

  (3)、观察者模式符合“对修改关闭,对扩展开放”的设计原则。

  2.缺点

  (1)、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会话费很多时间。

  (2)、观察者只知道被观察者发生了变化,但并不知道被观察者是如何发生变化的。

七、使用场景

  1、一个抽象模型有两个方面,其中一个方面依赖另一个方面。将这些方面封装在独立的对象中使它们可以各自独立的改变和复用。

  2、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少个对象将发生改变,可以降低对象之间的耦合度。

  3、一个对象必须通知其他对象,但而不知道这些对象是谁。就是对象之间的松耦合

八、总结

  1、观察者模式定义了对象之间一对多关系。

  2、有多个观察者时,不可以依赖特定的通知次序。

  3、java有通用的观察者模式(java.util.Observable)。

  4、观察者与被观察者之间用松耦合方式结合。

  5、简单理解就是一个对象状态的改变要通知到其它对象。