文档章节

Java设计模式之观察者模式

断桥残雪断桥残雪
 断桥残雪断桥残雪
发布于 2015/07/23 20:36
字数 2079
阅读 464
收藏 19

观察者模式是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。这一模式中的关键对象是目标(Subject)和观察者(Obserber)。一个目标可以有任意个观察者,一旦目标状态发生改变,所有的观察者将得到通知。这种交互也称为发布-订阅

观察者模式的适用场景:

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

当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。

当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换言之,你不希望这些对象是紧密耦合的。

基本结构:

Subject<---------ConcreteSubject

Observer<----------ConcreteObserver

Subject(目标):目标知道它的观察者,可以有任意个观察者;提供attach()、detach()、notifyObservers()方法接口。

ConcreteSubject(具体目标):写入具体目标对象的状态或内容(getter and setter方法),并且在它们的状态发生改变时会向它的观察者发出通知。

Observer(观察者):定义目标发生改变时需获得通知的对象定义一个接口,Update()方法。

ConcreteObserver(具体观察者):维护一个指向ConcreteSubject对象的引用;存储观察者自身的状态,实现Observer接口使自身状态与目标状态保持一致。

观察者模式允许独立的改变目标和观察者。可以单独复用目标对象而无需同时复用其观察者,反之亦然。

观察者模式的优点:目标与观察者之间的抽象耦合,耦合与抽象达到最小;支持广播更新。

观察者模式的缺点:容易引起误广播,即其中一个观察者修改了目标的状态,则其他观察者也收到了该更新。

观察者模式的实现需要考虑以下几个关键点:1.创建目标到其观察者之间的映射;2.观察多个目标;3.谁触发更新;4.对已删除目标的悬挂引用;5.在发出通知之前一定要确保目标的状态自身是一致的;6.避免特定于观察者的更新协议——推/拉模型:推模型是指目标向观察者发送关于改变的详细信息,而不管他们需要与否。拉模型是指目标除最小通知外什么也不送出,而此后由观察者显示地向目标询问细节。7.显示地指定感兴趣的改变。即可以让观察者对感兴趣的目标才接受通知。


下面通过一段代码来演示一下观察者模式的应用案例:

Subject.java

package com.observer.classic;

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

/**
 * 观察者模式
 * 目标对象
 * @author zzw
 */
public class Subject {

	//不同兴趣点观察者线性列表
	private List<Observer> observersAll = new ArrayList<Observer>();
	
	private List<Observer> observersMusic = new ArrayList<Observer>();
	
	private List<Observer> observersSport = new ArrayList<Observer>();
	
	private List<Observer> observersMovie = new ArrayList<Observer>();
	
	//增加观察者
	public void attach(Observer observer) {
		String interest = observer.getObserverInterest();
		if("All".equals(interest)) {
			observersAll.add(observer);
		} else if("Music".equals(interest)) {
			observersMusic.add(observer);
		} else if("Sport".equals(interest)) {
			observersSport.add(observer);
		} else if("Movie".equals(interest)) {
			observersMovie.add(observer);
		}
	}
	
	//删除观察者
	public void detach(Observer observer) {
		String interest = observer.getObserverInterest();
		if("All".equals(interest)) {
			observersAll.remove(observer);
		} else if("Music".equals(interest)) {
			observersMusic.remove(observer);
		} else if("Sport".equals(interest)) {
			observersSport.remove(observer);
		} else if("Movie".equals(interest)) {
			observersMovie.remove(observer);
		}
	}
	
	
	public void notifyObservers(String subjectTag) {
		if("Music".equals(subjectTag)) {
			for(Observer observer : observersMusic) {
				observer.update(this);
			}
		} else if("Sport".equals(subjectTag)) {
			for(Observer observer : observersSport) {
				observer.update(this);
			}
		} else if("Movie".equals(subjectTag)) {
			for(Observer observer : observersMovie) {
				observer.update(this);
			}
		}
		
		for(Observer observer : observersAll) {
			observer.update(this);
		}
	}
}


Observer.java

package com.observer.classic;
/**
 * 观察者接口
 * @author zzw
 *
 */
public interface Observer {

	public void update(Subject subject);
	
	public String getObserverInterest();
}

ConcreteSubject.java

package com.observer.classic;
/**
 * 具体的目标对象
 * 负责把目标的状态信息存入到相应的观察者身上
 * @author zzw
 */
public class ConcreteSubject extends Subject {

	//相应目标的状态(内容)
	private String subjectState;
	
	//目标通知的分类标签
	private String subjectTag;
	
	public String getSubjectTag() {
		return subjectTag;
	}


	public String getSubjectState() {
		return subjectState;
	}

	public void setSubjectState(String subjectState,String subjectTag) {
		this.subjectState = subjectState;
		this.subjectTag = subjectTag;
		this.notifyObservers(subjectTag);
	}

	
	
}

ConcreteObserver.java

package com.observer.classic;

public class ConcreteObserver implements Observer {
	//观察者姓名
	private String observerName;
	
	//观察者兴趣点
	private String observerInterest;
	
	//观察者状态信息
	private String observerState;
	
	
	public String getObserverName() {
		return observerName;
	}

	public void setObserverName(String observerName) {
		this.observerName = observerName;
	}
	
	public String getObserverState() {
		return this.observerState;
	}
	
	public String getObserverInterest() {
		return observerInterest;
	}
	
	public void setObserverInterest(String observerInterest) {
		this.observerInterest = observerInterest;
	}
	
	
	@Override
	public void update(Subject subject) {
		//获取目标状态,以保持更新
		observerState = ((ConcreteSubject) subject).getSubjectState();
		System.out.println(observerName + "收到了一条通知:" + observerState);
	}


}


Test.java

package com.observer.classic;

public class Test {

	public static void main(String[] args) {
		
		
		// 创建推送内容1
		ConcreteSubject concreteSubject = new ConcreteSubject();
		
		// 创建观察者(即待通知对象)
		ConcreteObserver concreteObserver1 = new ConcreteObserver();
		concreteObserver1.setObserverName("张三");
		concreteObserver1.setObserverInterest("All");
		
		ConcreteObserver concreteObserver2 = new ConcreteObserver();
		concreteObserver2.setObserverName("李四");
		concreteObserver2.setObserverInterest("Music");
		
		ConcreteObserver concreteObserver3 = new ConcreteObserver();
		concreteObserver3.setObserverName("王二麻子");
		concreteObserver3.setObserverInterest("Sport");
		
		ConcreteObserver concreteObserver4 = new ConcreteObserver();
		concreteObserver4.setObserverName("刘老五");
		concreteObserver4.setObserverInterest("Movie");
		
		//注册观察者
		concreteSubject.attach(concreteObserver1);
		concreteSubject.attach(concreteObserver2);
		concreteSubject.attach(concreteObserver3);
		concreteSubject.attach(concreteObserver4);
		
		//发布通知1
		System.out.println("消息一:");
		concreteSubject.setSubjectState("周杰伦发了一张新专辑《哎哟,不错哦》","Music");
		
		//发布通知2
		System.out.println("消息二:");
		concreteSubject.setSubjectState("姚明宣布退役了","Sport");
		
		//发布通知3
		System.out.println("消息三:");
		concreteSubject.setSubjectState("新电影《栀子花开》上映了","Movie");
		
	}
}

运行结果

消息一:

李四收到了一条通知:周杰伦发了一张新专辑《哎哟,不错哦》

张三收到了一条通知:周杰伦发了一张新专辑《哎哟,不错哦》

消息二:

王二麻子收到了一条通知:姚明宣布退役了

张三收到了一条通知:姚明宣布退役了

消息三:

刘老五收到了一条通知:新电影《栀子花开》上映了

张三收到了一条通知:新电影《栀子花开》上映了


下面使用另外一种方法来实现有区分的观察者,即把notifyObservers方法交由子类去实现,而父类仅仅提供抽象方法。

WeatherSubject.java

package com.observer.advanced;
/**
 * 观察者模式的高级
 * 允许推送内容到特定的观察者
 * notifyObservers()在子类中实现
 */
import java.util.ArrayList;
import java.util.List;

public abstract class WeatherSubject {

	List<Observer> observers = new ArrayList<Observer>();
	
	public void attach(Observer observer) {
		observers.add(observer);
	}
	
	public void detach(Observer observer) {
		observers.remove(observer);
	}
	
	//留到子类中去实现
	protected abstract void notifyObservers();
}

ConcreteWeatherSubject.java

package com.observer.advanced;



public class ConcreteWeatherSubject extends WeatherSubject {

	//目标状态
	private String subjectState;
	
	
	public ConcreteWeatherSubject(String subjectState) {
		super();
		this.subjectState = subjectState;
	}

	public String getSubjectState() {
		return subjectState;
	}

	public void setSubjectState(String subjectState) {
		this.subjectState = subjectState;
		
		//一旦目标得到修改,所有的观察者都会收到通知
		this.notifyObservers();
	}

	@Override
	protected void notifyObservers() {
		/**
		 * 由于张三只接受下雪的天气预报,李四只接受下雨的天气预报,其他观察者无限制
		 */
		for(Observer observer : observers) {
			if("下雪".equals(subjectState)) {
				if("张三".equals(observer.getObserverName()))
					observer.update(this);
				else if(!"李四".equals(observer.getObserverName()))
					observer.update(this);
			}
			else if("下雨".equals(subjectState)) {
				if("李四".equals(observer.getObserverName()))
					observer.update(this);
				else if(!"张三".equals(observer.getObserverName()))
					observer.update(this);
			}
			else
				if(!"李四".equals(observer.getObserverName())&&!"张三".equals(observer.getObserverName()))
					observer.update(this);
		}
	}

}

Observer.java

package com.observer.advanced;

public interface Observer {

	public void update(WeatherSubject subject);
	
	//设置观察者名称
	public String getObserverName();
	
	public void setObserverName(String observerName);
	
	public String getObserverSex();
	
	public void setObserverSex(String observerSex);
	
	public String getObserverAge();
	
	public void setObserverAge(String observerAge);
}

ConcreteObserver.java

package com.observer.advanced;

public class ConcreteObserver implements Observer {

	private String observerName;
	
	private String observerSex;
	
	private String observerAge;
	
	
	public ConcreteObserver(String observerName, String observerSex,
			String observerAge) {
		super();
		this.observerName = observerName;
		this.observerSex = observerSex;
		this.observerAge = observerAge;
	}

	@Override
	public void update(WeatherSubject subject) {
		// 推送内容
		
		String content = ((ConcreteWeatherSubject)subject).getSubjectState();
			System.out.println(this.getObserverName()+"("+this.getObserverSex()+","+this.getObserverAge()+")收到了一条消息:"+content);
	}

	@Override
	public String getObserverName() {
		return observerName;
	}

	@Override
	public void setObserverName(String observerName) {
		this.observerName = observerName;
	}

	public String getObserverAge() {
		return observerAge;
	}

	public void setObserverAge(String observerAge) {
		this.observerAge = observerAge;
	}

	public String getObserverSex() {
		return observerSex;
	}

	public void setObserverSex(String observerSex) {
		this.observerSex = observerSex;
	}

}

Test.java

package com.observer.advanced;

public class Test {

	public static void main(String[] args) {
		//新建三个观察者
		Observer concreteObserver1 = new ConcreteObserver("张三", "男", "22岁");
		Observer concreteObserver2 = new ConcreteObserver("李四", "男", "30岁");
		Observer concreteObserver3 = new ConcreteObserver("玛丽", "女", "25岁");
	
		//新建目标内容:下雪
		WeatherSubject concreteWeatherSubject1 = new ConcreteWeatherSubject("下雪");
		concreteWeatherSubject1.attach(concreteObserver1);
		concreteWeatherSubject1.attach(concreteObserver2);
		concreteWeatherSubject1.attach(concreteObserver3);
		concreteWeatherSubject1.notifyObservers();
		
		//新建目标内容:下雨
		WeatherSubject concreteWeatherSubject2 = new ConcreteWeatherSubject("下雨");
		concreteWeatherSubject2.attach(concreteObserver1);
		concreteWeatherSubject2.attach(concreteObserver2);
		concreteWeatherSubject2.attach(concreteObserver3);
		concreteWeatherSubject2.notifyObservers();
		
		//新建目标内容:晴天
		WeatherSubject concreteWeatherSubject3 = new ConcreteWeatherSubject("晴天");
		concreteWeatherSubject3.attach(concreteObserver1);
		concreteWeatherSubject3.attach(concreteObserver2);
		concreteWeatherSubject3.attach(concreteObserver3);
		concreteWeatherSubject3.notifyObservers();
	
	}
}


运行结果:

张三(男,22岁)收到了一条消息:下雪

玛丽(女,25岁)收到了一条消息:下雪

李四(男,30岁)收到了一条消息:下雨

玛丽(女,25岁)收到了一条消息:下雨

玛丽(女,25岁)收到了一条消息:晴天


© 著作权归作者所有

断桥残雪断桥残雪
粉丝 53
博文 139
码字总数 94909
作品 0
广州
程序员
私信 提问
设计模式 2014-12-19

book: 阎宏《JAVA与模式》 架构设计栏目 http://blog.csdn.net/enterprise/column.html 概要: http://bbs.csdn.net/forums/Embeddeddriver 23种设计模式分别是: 1.单例模式 2.工厂方法模式...

jayronwang
2014/12/19
305
0
设计模式15——Template Method设计模式

Template Method模板方法设计模式定义一个操作中算法的骨架,将具体步骤的执行延迟到子类中实现。Java中的抽象类就是使用了模板方法设计模式。模板方法设计模式结构如下: 以文档处理为例,T...

小米米儿小
2014/01/24
234
0
简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别

转载:原地址http://www.cnblogs.com/zhangchenliang/p/3700820.html 简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别 结合简单示例和UML图,讲解工厂模式简单原理。 一、引子 话说...

法斗斗
2018/05/08
316
0
策略模式与SPI机制,到底有什么不同?

这里说的策略模式是一种设计模式,经常用于有多种分支情况的程序设计中。例如我们去掉水果皮,一般来说对于不同的水果,会有不同的拨皮方式。此时用程序语言来表示是这样的: 如上面代码所写...

陈树义
2018/09/03
0
0
学了那么多年设计模式依然不会用!那可真蠢!

什么是设计模式? 设计模式(Design Pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决...

GitChat技术杂谈
2018/10/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

IT兄弟连 HTML5教程 CSS3属性特效 边框

通过CSS3,我们能够创建圆角边框,向矩形添加阴影,使用图片来绘制边框。并且不需使用设计软件,比如photoshop。 1 边框图片border-image border-image为边框应用图片,顾名思义就是为图片应...

老码农的一亩三分地
22分钟前
4
0
3个例子详解C++ 11 中push_back 和 emplace_back差异

本文首发于个人博客https://kezunlin.me/post/b83bc460/,欢迎阅读最新内容! cpp11 push_back and emplace_back Guide case1 #include <iostream>#include <vector>class A{public: A......

kezunlin
23分钟前
5
0
OSChina 周五乱弹 —— 你已经是个成熟的熊猫了

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @Sharon啊 :#今日歌曲推荐# 分享黑鸭子的单曲《羞答答的玫瑰静悄悄的开》 《羞答答的玫瑰静悄悄的开》- 黑鸭子 手机党少年们想听歌,请使劲儿...

小小编辑
今天
488
8
结合Spring Security进行web应用会话安全管理

在本文中,将为大家说明如何结合Spring Security 和Spring Session管理web应用的会话。 一、Spring Security创建使用session的方法 Spring Security提供4种方式精确的控制会话的创建: alwa...

fightinging
今天
6
0
83、Mybatis和Hibernate重要区别

Mybatis;入门简单,程序容易上手开发,节省开发成本。Mybatis需要程序猿自己编写sql语句,是一个不完全的ORM框架,对sql修改和优化非常容易实现。 Mybatis适合开发需求变更频繁的系统,比如...

lianbang_W
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部