观察者模式是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。这一模式中的关键对象是目标(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岁)收到了一条消息:晴天