什么是观察者模式
观察者一般可以看做是第三者,比如在学校上自习的时候,大家肯定都有过交头接耳、各种玩耍的经历,这时总会有一个“放风”的小伙伴,当老师即将出现时及时“通知”大家老师来了。再比如,拍卖会的时候,大家相互叫价,拍卖师会观察最高标价,然后通知给其它竞价者竞价,这就是一个观察者模式。
对于观察者模式而言,肯定有观察者和被观察者之分。比如在一个目录下建立一个文件,这时系统会通知目录管理器增加目录,并通知磁盘减少空间,在这里,文件就是观察者,目录管理器和磁盘就是被观察者。
观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。UML结构图如下:
13931e0b5e88e1dfb6961f3dc62e1d9.png
其中,Subject类是主题,它把所有对观察者对象的引用文件存在了一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供了一个接口,可以增加和删除观察者对象;Observer类是抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己;ConcreteSubject类是具体主题,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知;ConcreteObserver是具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协同。
主题Subject
首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用CopyOnWriteArrayList是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。
public class Subject { //观察者数组 private CopyOnWriteArrayList<Observer> copyOnWriteArrayList = new CopyOnWriteArrayList<>(); //增加一个观察者 public void addObserver(Observer observer) { this.copyOnWriteArrayList.add(observer); } //删除一个观察者 public void deleteObserver(Observer observer) { this.copyOnWriteArrayList.remove(observer); } //通知所有观察者 public void notifyObserver() { copyOnWriteArrayList.forEach(item -> { item.update(); }); } }
抽象观察者
观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者。
public interface Observer { //更新 void update(); }
具体主题
继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种
public class ConcreteSubject extends Subject { //具体业务 public void doSomething(){ //... super.notifyObserver(); } }
具体观察者
public class ConcreteObserver implements Observer{ @Override public void update() { System.out.println("收到消息,进行处理"); } } //客户端 public class Client { public static void main(String[] args) { //创建一个主题 ConcreteSubject subject = new ConcreteSubject(); //定义一个观察者 Observer observer=new ConcreteObserver(); //观察 subject.addObserver(observer); subject.doSomething(); } }
观察者模式的实现
下面举一个具体实例,假设上班时间有一部分同事在看股票,一部分同事在看NBA,这时老板回来了,前台通知了部分同事老板回来了,这些同事及时关闭了网页没被发现,而没被通知到的同事被抓了个现行,被老板亲自“通知”关闭网页,UML图如下:
image.png
通知者接口
public interface SubjectDemo { //增加 void attach(ObserverDemo observerDemo); //删除 void detach(ObserverDemo observerDemo); //通知 void notifyObservers(); //状态 void setAction(String action); String getAction(); }
观察者接口
public abstract class ObserverDemo { protected String name; protected SubjectDemo subjectDemo; public ObserverDemo(String name, SubjectDemo subjectDemo) { this.name = name; this.subjectDemo = subjectDemo; } public abstract void update(); }
具体通知者
前台Secretary和老板Boss作为具体通知者,实现Subject接口。这里只给出Secretary类的代码,Boss类与之类似。
public class Secretary implements SubjectDemo { //同事列表 private List<ObserverDemo> observerDemos = new CopyOnWriteArrayList<>(); private String action; @Override public void attach(ObserverDemo observerDemo) { observerDemos.add(observerDemo); } @Override public void detach(ObserverDemo observerDemo) { observerDemos.remove(observerDemo); } @Override public void notifyObservers() { observerDemos.forEach(item -> { item.update(); }); } @Override public void setAction(String action) { this.action = action; } @Override public String getAction() { return action; } }
具体观察者
public class StockObserver extends ObserverDemo { public StockObserver(String name, SubjectDemo subjectDemo) { super(name, subjectDemo); } @Override public void update() { System.out.println(subjectDemo.getAction()+"\n"+name+"关闭股票行情,继续工作"); } } public class NBAObserver extends ObserverDemo { public NBAObserver(String action, SubjectDemo subjectDemo) { super(action, subjectDemo); } @Override public void update () { System.out.println(subjectDemo.getAction() + "\n" + name + "关闭直播,继续工作"); } }
前台作为通知者进行通知
前台作为通知者,通知观察者。这里添加adam和tom到通知列表,并从通知列表中删除了adam,测试没在通知列表中的对象不会收到通知。
public class TestDemo { public static void main(String[] args) { //前台为通知者 Secretary secretary = new Secretary(); StockObserver observer = new StockObserver("adam", secretary); NBAObserver observer1 = new NBAObserver("tom", secretary); //前台通知 secretary.attach(observer); secretary.attach(observer1); //adam没有被前台通知到,所以被老板抓个现行 secretary.detach(observer); //老板回来了 secretary.setAction("小心!Boss回来了!"); //发通知 secretary.notifyObservers(); } }