观察者模式(Observer),属于行为型模式。又叫发布-订阅(Publish/Subscribe)模式、模型-视图 (Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个 主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
JDK自带的观察者模式
JDK内部已经实现了观察者模式,观察者需要实现Observer
接口,被观察者需要继承Observable
类。
下面以内容订阅者(观察者)订阅内容发布者(被观察者)的发布内容来举例,当内容发布者发布内容时,会马上将发布内容发送给订阅者去处理。
1、被观察者WeiboWriter
,需要继承Observable
基类:
import java.util.Observable;
public class WeiboWriter extends Observable {
public void write(String weibo) {
setChanged();
notifyObservers(weibo);
}
}
当内容发布者发布内容时,调用notifyObservers()
方法去通知观察者,内容以及发布。
在调用notifyObservers()
之前,需要先调用setChanged()
方法,否则将无法发送消息给观察者。
2、观察者WeiboReader
,需要实现Observer
接口,复写update()
方法:
import java.util.Observable;
import java.util.Observer;
public class WeiboReader implements Observer{
@Override
public void update(Observable o, Object obj) {
if(obj instanceof String) {
String objStr = (String) obj;
System.out.println("推送:" + obj);
}
}
}
当内容发布者(被观察者)发布内容时,内容订阅者(观察者)会在update()
方法内收到消息,并处理。
3、编写一个sina
类,测试:
public class Sina {
public static void main(String[] args) {
WeiboWriter writer = new WeiboWriter();
WeiboReader reader = new WeiboReader();
writer.addObserver(reader);
writer.write("周小伦最新专辑,马上上线!");
}
}
结果打印:
推送:周小伦最新专辑,马上上线!
被观察者无法继承Observable
的解决办法
有时候,我们的被观察者由于已经继承其他父类而无法继承Observable
时,我们可以采用组合的方式解决这个问题——在被观察者内部创建一个Observable
类,来中转发送消息。
1、新建一个类MyObservable
,继承Observable
类:
import java.util.Observable;
public class MyObservable extends Observable{
public synchronized void setChanged() {
super.setChanged();
}
}
需要手动创建一个MyObservable
的原因在于,Observable
类的setChanged()
方法由于protect
权限设置,无法在外部类调用,因此我们需要采用如上这种方式扩大其使用权限。
2、在被观察者内部创建一个MyObservable
实例对象,通过MyObservable
实例对象来达到发送订阅消息的目的:
import java.util.Observable;
import java.util.Observer;
public class WeiboWriter extends JavaWorker {
private MyObservable observable = new MyObservable();
public void write(String weibo) {
observable.setChanged();
observable.notifyObservers(weibo);
}
public void addObserver(Observer observer) {
observable.addObserver(observer);
}
}
总结
JDK内部实现的观察者模式实现起来很方便,并且是线程安全的,缺点是被观察者需要继承Observable
或者通过其他方式间接继承此类。
自己实现观察者模式
这里以公司发工资给员工举例,会计是被观察者,员工是观察者,员工观察会计发工资这个动作,当会计发工资后,员工第一时间得到通知。
1、编写接口Accounting
,模拟发工资的动作:
public interface Accounting {
public void payOff();
public void addWorker(Worker woker);
public void removeWorker(Worker woker);
}
2、编写Worker
接口,模拟员工在拿到工资后的处理:
public interface Worker {
public void wagaWaga(int salary);
}
3、编写Boss
类,实现Accounting
接口(小公司,老板就是会计):
import java.util.ArrayList;
import java.util.List;
public class Boss implements Accounting{
private List<Worker> workers;
public Boss() {
workers = new ArrayList<>();
}
@Override
public void payOff() {
for (int i = 0; i < workers.size(); i++) {
Worker worker = workers.get(i);
worker.wagaWaga((i + 1) * 1000);
}
}
@Override
public void addWorker(Worker woker) {
if(!workers.contains(woker)) {
workers.add(woker);
}
}
@Override
public void removeWorker(Worker woker) {
if(workers.contains(woker)) {
workers.remove(woker);
}
}
}
4、编写3个员工,JavaWorker
、PHPWorker
和CWorker
,实现Worker
接口:
public class JavaWorker implements Worker{
@Override
public void wagaWaga(int salary) {
System.out.println("JavaWorker 本月工资:" + salary);
}
}
5、编写Company
类,测试:
public class Company {
public static void main(String[] args) {
Accounting accounting = new Boss();
Worker javaWorker = new JavaWorker();
Worker phpWorker = new PHPWorker();
Worker cWorker = new CWorker();
accounting.addWorker(javaWorker);
accounting.addWorker(phpWorker);
accounting.addWorker(cWorker);
accounting.payOff();
}
}
测试结果:
JavaWorker 本月工资:1000
PHPWorker 本月工资:2000
CWorker 本月工资:3000
网友评论