观察者模式,又叫做发布–订阅模式(Publish/Subscribe)模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生改变时,会通知所有的观察者对象,使他们能够自动更新自己。
观察者模式(Observer)结构图
// 抽象通知者
public abstract class Subject {
private List<Observer> list = new ArrayList<>();
// 状态
protected String subjectState;
// 增加观察者
public void attatch(Observer observer) {
list.add(observer);
}
// 减少观察者
public void detach(Observer observer) {
list.remove(observer);
}
// 通知观察者
public void notifyObserver() {
for (Observer item : list) {
item.update();
}
}
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
}
// 抽象观察者
public abstract class Observer {
public abstract void update();
}
// 具体主题或具体通知者
public class ConcreteSubject extends Subject {
}
// 具体观察者
public class ConcreteObserver extends Observer {
private String name;
private Subject sub;
public ConcreteObserver(String name, Subject sub) {
this.name = name;
this.sub = sub;
}
@Override
public void update() {
System.out.println("观察者"+this.name + "的新状态是"+this.sub.getSubjectState());
}
}
客户端调用
Subject subject = new ConcreteSubject();
subject.attatch(new ConcreteObserver("nameX", subject));
subject.attatch(new ConcreteObserver("nameY", subject));
subject.attatch(new ConcreteObserver("nameZ", subject));
subject.notifyObserver();
那么现在举一个简单的例子:某公司中,某部门几个员工在上班期间趁着老板不在,炒股、看NBA等等开始摸鱼,并且通过买一些小零食贿赂前台小姐姐,当老板回来时通知他们一声。使用观察者模式如何实现呢?
那么首先分清楚角色划分,具体主题或具体通知者就是我们的前台小姐姐,那么具体观察者就是部门的几个员工。
我们的抽象通知者Subject类基本是不需要改变的,直接拿上面的的代码就行,然后设置个名称区分下就行。
// 抽象通知者
public abstract class Subject {
private List<Observer> list = new ArrayList<>();
protected String subjectState;
protected String name;
public Subject(String name) {
this.name = name;
}
// 增加观察者
public void attatch(Observer observer) {
list.add(observer);
}
// 减少观察者
public void detach(Observer observer) {
list.remove(observer);
}
// 通知观察者
public void notifyObserver() {
for (Observer item : list) {
item.update();
}
}
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
}
// 前台小姐姐
public class Secretary extends Subject {
public Secretary(String name) {
super(name);
}
}
// 看股票的同事
public class StockObserver extends Observer {
public StockObserver(String name, Subject sub) {
super(name, sub);
}
@Override
public void update() {
System.out.println(super.sub.name + ":"+super.sub.getSubjectState() + "!" + super.name + "快关闭股票行情,开始工作");
}
}
// 看NBA的同事
public class NBAObserver extends Observer {
public NBAObserver(String name, Subject sub) {
super(name, sub);
}
@Override
public void update() {
System.out.println(super.sub.name + ":"+super.sub.getSubjectState() + "!" + super.name + "快关闭NBA直播,开始工作");
}
}
Subject subject = new Secretary("前台小姐姐");
subject.attatch(new StockObserver("张三", subject));
subject.attatch(new StockObserver("李四", subject));
subject.attatch(new NBAObserver("王五", subject));
subject.setSubjectState("老板回来了");
subject.notifyObserver();
结果:
前台小姐姐:老板回来了!张三快关闭股票行情,开始工作
前台小姐姐:老板回来了!李四快关闭股票行情,开始工作
前台小姐姐:老板回来了!王五快关闭NBA直播,开始工作
实际上,java已经为观察者模式准备好了相关的接口和抽象类了。观察者接口java.util.Observer
和通知者java.util.Observable
。有了这些Java内置的代码,我们只需要扩展或继承Observable,并且告诉它什么时候应该通知观察者就OK了。
那么后边考虑到如果有多个通知者的情况下,为了避免需要在update()方法中对通知者进行强转,中间又加了一层Subject类。
代码结构图
那么看看具体实现过程:
// 抽象通知者
public abstract class Subject extends Observable {
private List<Observer> list = new ArrayList<>();
protected String subjectState;
protected String name;
public Subject(String name) {
this.name = name;
}
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
super.setChanged(); // 改变通知者的状态
super.notifyObservers(); // 调用父类的方法,通知所有的观察者
}
}
// 前台小姐姐
public class Secretary extends Subject {
public Secretary(String name) {
super(name);
}
}
// 看股票的同事
public class StockObserver implements Observer {
protected String name;
public StockObserver(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
Subject sub = (Subject) o;
System.out.println(sub.name + ":"+sub.getSubjectState() + "!" + this.name + "快关闭股票行情,开始工作");
}
}
// 看NBA的同事
public class NBAObserver implements Observer {
protected String name;
public NBAObserver(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
Subject sub = (Subject) o;
System.out.println(sub.name + ":"+sub.getSubjectState() + "!" + this.name + "快关闭NBA直播,开始工作");
}
}
Subject subject = new Secretary("前台小姐姐");
subject.addObserver(new StockObserver("张三"));
subject.addObserver(new StockObserver("李四"));
subject.addObserver(new NBAObserver("王五"));
subject.setSubjectState("老板回来了");
subject.notifyObservers();
使用观察者模式的动机是什么呢?
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。
当一个对象的改变需要同时改变其他对象时,而它不知道具体有多少个对象有待改变时,应该考虑使用观察者模式。
抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
观察者模式所作的工作其实就是在解耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。