本文主要为大家介绍观察者模式的定义,特点以及如何实现一个观察者模式。在手动实现观察者模式之后,也会为大家介绍JDK内置观察者模式的使用。
定义
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式的特点就是观察者和被观察者是抽象耦合的。被观察者只知道观察者对象实现了公共的接口(Observer),不知道观察者对象具体是谁,更加不知道它们的行为。另外,我们可以任意的添加或者删除观察者,被观察者不会受到任何影响。使用观察者模式,我们可以设计出很好的松耦合系统。
使用场景
1.当一个对象更新时,需要同步其它对象的状态,并且其它对象的数量是不确定的。
2.对象只需要将自身更新状态通知过去,不需要知道其它对象细节。
自定义实现
在实现观察者模式的时候,我们通常需要依赖两种类型的对象:观察者和被观察者。通常我们又把被观察者叫做主题,观察者又被叫做订阅者。大家试想一下,如果我们自己实现观察者模式,我们应该怎样开始呢?上面已经介绍到观察者模式中的对象是一对多的关系,不难猜测出这种关系应该是被观察者 1—— n 观察者,要想实现这种关系,那么被观察者对象就需要包含观察者对象的集合,这样才能在自身内部发生变化的时候,把这种变化传递给观察者对象。观察者需要向被观察者注册、注销自己,那么就需要包含被观察者的对象。通过我们的推测,代码大概是这个样子的:
被观察者类(主题):
public class Subject {
//用于判断主题(被观察者)自身状态是否改变
boolean changed = false;
//具体内容
String message;
//用于存放注册的观察者(订阅者)
private List<Observer> observers = new ArrayList<>();
public void add(Observer observer) {
this.observers.add(observer);
}
public void remove(Observer observer) {
this.observers.remove(observer);
}
public void setMessage(String message) {
this.message = message;
this.changed = true;
notifyObservers();
}
//用于通知所有观察者
public void notifyObservers() {
if (changed) {
for (Observer observer : observers) {
observer.update(this.message);
}
this.changed = false;
}
}
}
观察者类(订阅者):
public interface Observer {
void update(String message);
}
//观察者一
class FirstObserver implements Observer {
//被观察类
private Subject subject;
public FirstObserver(Subject subject) {
this.subject = subject;
this.subject.add(this);
}
@Override
public void update(String message) {
System.out.println("观察者一接受到消息:" + message);
}
}
//观察者二
class SecondObserver implements Observer {
//被观察类
private Subject subject;
public SecondObserver(Subject subject) {
this.subject = subject;
this.subject.add(this);
}
@Override
public void update(String message) {
System.out.println("观察者二接受到消息:" + message);
}
}
测试:
Subject subject = new Subject();
FirstObserver firstObserver = new FirstObserver(subject);
SecondObserver secondObserver = new SecondObserver(subject);
subject.setMessage("华为投诉美国政府");
subject.setMessage("5G时代即将来临");
上述是我们自己实现的一个简单版的观察者模式,核心内容基本就是这些,但是我们仔细观察,这里面还是有很大的问题的,例如我们的集合使用的是List,这样可能会导致一个对象被注册多次,另外我们上述的注册和注销操作都没有考虑线程不安全的情况,在多线程操作的情况下,可能出现异常。当然,当我们知道这些问题之后,可以继续优化,但是有这个必要吗?其实,JDK已经为广大开发者提供了观察者与被观察者的实现,我们自己写观察者模式,只是为了加深对它的了解,更多的时候不需要我们自己去实现,当然,自己实现也比较简单。
JDK观察者模式实现
上述我们所实现的观察者模式,是一个典型的推模式,也就是被观察者主动推送消息给观察者对象,无需观察者手动获取消息,这是我们通常使用的方式。在某些特殊的情况下,各个观察者可能需要的消息内容不一样,没有必要全部推送,这个时候就可以使用拉模式。JDK已经很好的实现了两种方式,现在我们就来具体使用一下。
被观察者:通过继承Observable来实现,已经封装好注册、注销观察者相关方法。
public class NewsObservable extends Observable {
//技术类新闻
private String technology;
//娱乐新闻
private String entertainment;
//军事新闻
private String military;
public void changed() {
//改变自身状态
setChanged();
//这里使用推模式传递消息,通知完毕后,改变状态会被清空
notifyObservers(this.technology);
}
//用于修改新闻
public void setNews(String technology, String entertainment, String military) {
this.technology = technology;
this.entertainment = entertainment;
this.military = military;
changed();
}
public String getTechnology() {
return technology;
}
public String getEntertainment() {
return entertainment;
}
public String getMilitary() {
return military;
}
}
观察者:通过implements Observer实现观察者。
public class CustomObserver implements Observer {
//Observable就是我们的被观察对象
@Override
public void update(Observable o, Object arg) {
System.out.println("获得消息:" + arg);
}
}
测试:
NewsObservable newsObservable = new NewsObservable();
CustomObserver customObserver1 = new CustomObserver();
CustomObserver customObserver2 = new CustomObserver();
//用于注册观察者
newsObservable.addObserver(customObserver1);
newsObservable.addObserver(customObserver2);
newsObservable.setNews("技术新闻", "娱乐新闻", "军事新闻");
//用于注销观察者
newsObservable.deleteObserver(customObserver2);
newsObservable.setNews("技术新闻1", "娱乐新闻1", "军事新闻1");
上述在被观察者类中,我们使用notifyObservers(Object args);方法进行通知观察者对象,该方法在传递参数的时候就是推模式,不传递参数的时候就是拉模式。如果我们想使用拉模式获取消息,如下操作:
@Override
public void update(Observable o, Object arg) {
NewsObservable newsObservable= (NewsObservable)o;
System.out.println("获得消息:" + newsObservable.getEntertainment()+":"+newsObservable.getTechnology());
}
采用JDK内置的观察者模式实现,我们无需关注观察者的注册、注销等相关行为,并且内置的观察者模式采用Vector保存观察者对象以及使用synchronized,保证了在多线程使用中能够做到线程安全,大家有空可以去看看源码,比较简单。
总结
观察者模式有助于我们建立松耦合的系统,针对不同的业务环境,大家可以采用推模式或拉模式。JDK提供内置观察者模式供开发者使用,若不能满足大家的需求,手动实现也是比较简单。