观察者设计模式(Observer Pattern):又称为发布订阅模式(Publish/Subsribe),它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
观察者模式的核心就是将观察者与被观察者解耦,类似于消息的广播发送,使得被观察者的变动能通知到观察者


观察者模式主要包含如下4个角色:

【案例】
定义一个消息处理模型,小灰、小蓝订阅自己喜欢观看的频道,当这些频道退出新的节目时需通知已经订阅过该频道的用户
package com.pattern.demo01_手动实现观察者设计模式;
/**
* @author lscl
* @version 1.0
* @intro: 抽象主题
*/
public interface ISubject {
// 主题名称
public String getName();
// 增加订阅者
public void attach(IObserver observer);
// 删除订阅者
public void detach(IObserver observer);
// 广播消息
public void notify(String message);
}
package com.pattern.demo01_手动实现观察者设计模式;
import java.util.ArrayList;
import java.util.List;
/**
* @author lscl
* @version 1.0
* @intro: 具体主题(具体被观察者)
*/
public class TVSubject implements ISubject {
// 频道的名称
private String name;
// 储存订阅频道的用户
private List<IObserver> userList = new ArrayList();
public TVSubject(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void attach(IObserver observer) {
userList.add(observer);
}
@Override
public void detach(IObserver observer) {
userList.remove(observer);
}
@Override
public void notify(String message) {
for (IObserver observer : userList) {
observer.update(this, message);
}
}
}
package com.pattern.demo01_手动实现观察者设计模式;
/**
* @author lscl
* @version 1.0
* @intro: 抽象观察者类
*/
public interface IObserver {
// 更新信息方法
void update(ISubject subject, String message);
}
package com.pattern.demo01_手动实现观察者设计模式;
/**
* @author lscl
* @version 1.0
* @intro: 具体观察者(接收来自观察者的消息)
*/
public class UserObserver implements IObserver {
// 用户名
private String name;
public UserObserver(String name) {
this.name = name;
}
@Override
public void update(ISubject subject, String message) {
System.out.println("【" + name + "】接到来自【" + subject.getName() + "】的消息【" + message + "】");
}
}
package com.pattern.demo01_手动实现观察者设计模式;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01 {
public static void main(String[] args) {
// 创建主题(频道、被观察者)
ISubject subject = new TVSubject("CCTV");
// 创建用户(观察者)
UserObserver user1 = new UserObserver("小灰");
UserObserver user2 = new UserObserver("小蓝");
// 订阅CCTV频道
subject.attach(user1);
subject.attach(user2);
// 发送给那些订阅过CCTV频道的用户
subject.notify("今晚19.00播出新闻联播,敬请收看~");
}
}
运行效果:

在 Java 中,提供有java.util.Observable类来表示主题,java.util.Observer来表示观察者,借助这两个类我们可以很轻松的实现观察者模式;
Observable 类是一个抽象主题角色,它有一个 Vector 集合成员变量,用于保存所有要通知的观察者对象,常用方法如下:
void addObserver(Observer o):将新的观察者对象添加到集合中。
void notifyObservers(Object arg):调用集合中的所有观察者对象的update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
void setChange():Observable类内部有个变量changed,默认为false;调用该方法将其设置为true,表示目标对象发生了变化。然后调用notifyObservers()方法才会通知观察者。
Observer接口是抽象观察者角色,它监视目标对象的变化,当目标对象发生变化时,主题将会发送消息到观察者,并调用 update 方法,进行相应的工作。
package com.pattern.demo02_采用JDK的API实现;
import java.util.Date;
import java.util.Observable;
/**
* @author lscl
* @version 1.0
* @intro: 被观察者
*/
public class TVSubject extends Observable {
private String name;
public TVSubject(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void publish(String message) {
super.setChanged(); // 将changed设置为true(只有changed为true才能进行下面的广播消息)
super.notifyObservers(message); // 广播消息,观察者将会接收到消息
}
}
package com.pattern.demo02_采用JDK的API实现;
import java.util.Observable;
import java.util.Observer;
/**
* @author lscl
* @version 1.0
* @intro: 具体观察者
*/
public class UserObserver implements Observer {
private String name;
public UserObserver(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
/**
*
* @param subject: 订阅的主题
* @param message: 主题发送过来的消息
*/
@Override
public void update(Observable subject, Object message) {
TVSubject tvSubject = (TVSubject) subject;
System.out.println("【" + getName() + "】接收到来自【" + tvSubject.getName() + "】的一条消息【" + message + "】");
}
}
package com.pattern.demo02_采用JDK的API实现;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01 {
public static void main(String[] args) {
TVSubject subject = new TVSubject("马桶台");
subject.addObserver(new UserObserver("小灰"));
subject.publish("各位马桶粉们~今晚21.30播出《快乐大本营》请大家准时收看~");
}
}
运行效果:
