文档章节

观察者(Observer)模式

叶知秋
 叶知秋
发布于 2013/08/15 22:26
字数 1671
阅读 57
收藏 0

       观察者模式是对象的行为模式,又叫发布-订阅模式、模型-视图模式、源-监听模式、或从属者模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有的观察者对象,使它们能够自己更新自己。
       一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其他的对象做出相应的改变。能做到这一点的设计方案很多,但是为了使系统易于复用,应该选用低耦合度的设计方案。减少对象之间的耦合有助于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行为的协调一致,保证高度的协作。观测者模式是满足这一要求的各种设计方案中最重要的一种。
     
观察者模式的结构
      
下图是一个简单的示意性实现类图:
                                         
           观察者模式的静态结构可以从类图中看出,在这个观察者模式的实现中,有以下角色:
      (1)抽象主题(Subject)角色:主题角色把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫抽象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。
      (2)抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。
      (3)具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observer)。具体主题角色通常用一个具体子类实现。
      (4)具体观察者(ConcreteObserver)角色:存储于主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。 
       在类图中,从具体主题角色指向抽象观察者角色的合成关系,代表具体主题对象可以包含任意多个对抽象观察者对象的引用。由于java的抽象类是不可能有实例的,因此这些引用的的真实类型必定是ConcreteObserver类型,而这些引用的静态类型是Observer类型,这意味着主题对象不需要知道引用了哪些ConcreteObserver类型,而只知道抽象Observer类型,这就使得具体主题对象可以动态的维护一系列的对观察者对象的引用,并且在需要的时候调用每一个观察者的公有的update()方法。
       下面给出示意性代码:
 1.ject.java

package com.pattem.behavioral.Observer;

/**
 * 抽象主题角色
 * */
public interface Subject {
	
	//登记一个新的观察者对象
	public void attach(Observer observer);
	
	//删除一个已经登记过的观察者对象
	public void detach(Observer observer);
	
	//通知所有登记过的观察者对象
	public void notifyObserver();

}

2.ConcreteSubject.java

package com.pattem.behavioral.Observer;

import java.util.Enumeration;
import java.util.Vector;

import javax.sound.sampled.EnumControl;

public class ConcreteSubject implements Subject{

	private Vector<Observer> observersVector = new Vector<Observer>();
	
	public void attach(Observer observer) {
		observersVector.addElement(observer);
	}

	public void detach(Observer observer) {
		observersVector.removeElement(observer);
	}
	
	public void notifyObserver() {
		Enumeration enumeration = observers();
		while(enumeration.hasMoreElements()){
			((Observer)enumeration.nextElement()).update();
		}
	}
	
	public Enumeration observers(){
		return ((Vector)observersVector.clone()).elements();
	}

}

3.Observer.java

package com.pattem.behavioral.Observer;

/**
 * 抽象观察者角色
 * */
public interface Observer {
	
	//调用这个方法更新自己
	void update();

}

4.ConcreteObserver.java

package com.pattem.behavioral.Observer;

/**
 * 具体观察者角色
 * */
public class ConcreteObserver implements Observer{

	public void update() {
		
		//修改方法
		System.out.println("I am notified");
	}
}

         如果仔细考察主题功能的对象的功能时,可以发现它必须使用一个java集合来维护一个对所有的观察者对象的引用。而在前面所给出的实现里面,管理这个集合的方法是由抽象主题角色声明并由具体主题角色实现的。这才导致了类图中从具体主题角色到抽象观察者角色的连线。
       但是一个自然的问题就是,这些集合管理方法难道在每一个具体主题角色中都不同吗?答案是否定的。在大多数情况下,这些集合管理方法本身就是所有具体主题角色所共有的,因此这些方法连同集合本身都可以移动到抽象主题角色中去。同意由于notifyObserver()方法依赖集合对象,也可以移到抽象主题角色中去。  这就是第二种实现方案。其结构类图如下:   
                           
       下面给出抽象主题的源码:
Subject.java

package com.pattem.behavioral.Observer2;

import java.util.Enumeration;
import java.util.Vector;


/**
 * 抽象主题角色
 * */
public abstract class Subject {
	
	//保存对观察者对象的引用
	private Vector<Object> observerVector = new Vector<Object>();
	
	//登记一个新的观察者对象
	public void attach(Observer observer){
		observerVector.addElement(observer);
		System.out.println("Attach an observer");
	}

	//删除一个已经登记过的观察者对象
	public void detach(Observer observer){
		observerVector.removeElement(observer);
	}
	
	public void notifyObserver(){
		Enumeration<Object> enumeration = observers();
		while(enumeration.hasMoreElements()){
			((Observer)enumeration.nextElement()).update();
		}
	}
	//给出观察者聚集的 Enumeration对象
	public Enumeration<Object> observers(){
		return ((Vector)observerVector.clone()).elements();
	}
}

ConcreteSubject.java

package com.pattem.behavioral.Observer2;


/**
 * 具体主题角色
 * */
public class ConcreteSubject extends Subject {

	private String state;
	
	public void change(String state){
		this.state = state;
		this.notifyObserver();
	}
}

Client.java

package com.pattem.behavioral.Observer2;

/**
 * 客户端代码 Client
 * */
public class Client {
	
	private static ConcreteSubject subject;
	private static Observer observer;
	
	public static void main(String[] args) {
		
		//创建主题对象
		subject = new ConcreteSubject();
		//创建观察者对象
		observer = new ConcreteObserver();
		//将观察者对象登记到
		subject.attach(observer);
		//改变主题对象的状态
		subject.change("new state");
	}
}

     在运行时,这个客户端首先创建了具体主题类的实例,以及一个观察对象,然后它调用主题对象的attach()方法,将这个观察者对象向主题对象登记,也就是将它加入到主题对象的集合中去。



java语言提供对观察者模式的支持

     
在java语言的java.util库里面,提供了一个Observable的类和一个Observer的接口,构成java语言对观察者模式的支持。(详情,请参考java.util.Observable 类源码)

© 著作权归作者所有

共有 人打赏支持
叶知秋
粉丝 24
博文 85
码字总数 103570
作品 0
浦东
程序员
Java程序员从笨鸟到菜鸟之(三十六)大话设计模式(六)观察者模式

本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188 Observer模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对...

长平狐
2012/11/12
72
0
iOS-观察者模式

前言 与其说发布订阅是观察者模式的别名,还不如说发布订阅本质上是一种特殊的观察者模式;两种模式都主要是用于解除一个对象与多个对象之间的耦合,即不管有多少个监听者(observer),都不...

麦兜卖鱼丸
2016/08/15
45
0
设计模式17——Observer设计模式

Observer观察者设计模式用于将对象的变化通知给感兴趣的用户。在Observer模式中的角色为主题(subject)与观察者(observer),观察者订阅它感兴趣的主题,一个主题可以被多个观 察者订阅,当...

小米米儿小
2014/01/26
0
0
设计模式--观察者模式

观察者模式(observer): 示例: 观察者模式测试类ObserverTest.java 主题接口类Subject.java 观察者接口类Observer.java 猎头类HeadHunterImpl.java 求职者类JobSeekerImpl.java...

有钱有爱
2016/04/09
7
0
zookeeper 观察者-学习笔记

除了leader和follow模式之外,还有第三种模式:observer模式。 observer和follower在一些方面是一样的。详细点来讲,他们都向leader提交proposal。 但与follower不同,observer不参与投票的过...

1066897515
06/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

70.shell的函数 数组 告警系统需求分析

20.16/20.17 shell中的函数 20.18 shell中的数组 20.19 告警系统需求分析 20.16/20.17 shell中的函数: ~1. 函数就是把一段代码整理到了一个小单元中,并给这个小单元起一个名字,当用到这段...

王鑫linux
今天
2
0
分布式框架spring-session实现session一致性使用问题

前言:项目中使用到spring-session来缓存用户信息,保证服务之间session一致性,但是获取session信息为什么不能再服务层获取? 一、spring-session实现session一致性方式 用户每一次请求都会...

WALK_MAN
今天
5
0
C++ yield()与sleep_for()

C++11 标准库提供了yield()和sleep_for()两个方法。 (1)std::this_thread::yield(): 线程调用该方法时,主动让出CPU,并且不参与CPU的本次调度,从而让其他线程有机会运行。在后续的调度周...

yepanl
今天
4
0
Java并发编程实战(chapter_3)(线程池ThreadPoolExecutor源码分析)

这个系列一直没再写,很多原因,中间经历了换工作,熟悉项目,熟悉新团队等等一系列的事情。并发课题对于Java来说是一个又重要又难的一大块,除非气定神闲、精力满满,否则我本身是不敢随便写...

心中的理想乡
今天
34
0
shell学习之获取用户的输入命令read

在运行脚本的时候,命令行参数是可以传入参数,还有就是在脚本运行过程中需要用户输入参数,比如你想要在脚本运行时问个问题,并等待运行脚本的人来回答。bash shell为此提 供了read命令。 ...

woshixin
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部