【设计模式 - 19】之观察者模式(Observer)
1、模式简介
观察者模式的介绍:
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象的状态发生改变时,它的所有依赖者都会收到通知并自动更新。
发布者(被观察者) + 订阅者(观察者) = 观察者模式
观察者模式的优点:
- 观察者和被观察者是抽象耦合的;
- 观察者模式建立了一套触发机制(触发联动)。
观察者模式的缺点:
- 如果一个被观察者有很多直接或简介的观察者的话,将所有的观察者都通知到会花费很多时间;
- 如果在观察者和被观察者之间有循环依赖的话,被观察者会触发它们之间的的循环调用,可能会导致系统崩溃(因此,在观察者模式中应该避免观察者和被观察者之间的循环调用)。
观察者模式的适用场景:
- 当需要完成“触发联动”的功能时,即当一个对象的状态发生变化时,会触发与之相关联的多个对象状态的改变;
- 当观察者对象不需要知道主题对象(被观察者)的实现细节时。
观察者模式的UML图:
2、案例
在这个案例中,我们模拟报社的职能:
- 报社的业务就是出版报纸;
- 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,就会一直收到新报纸;
- 当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来;
- 只要报社还在运营,就会一直有人向他们订阅或取消订阅报纸。
2.1、手写观察者模式
手写的观察者模式的代码如下:
被观察者的抽象父类Subject代码如下:
public abstract class Subject { protected String name; protected Object thing; abstract void addObserver(Observer observer); abstract void removeObserver(Observer observer); abstract void notifyObservers(); public void setThing(Object thing) { this.thing = thing; System.out.println(this.name + "发布了一条消息:" + thing.toString()); notifyObservers(); } }
被观察者的具体类:报社类NewsOffice中的代码:
import java.util.ArrayList; import java.util.List; public class NewsOffice extends Subject { private List<Observer> observers; public NewsOffice() { super.name = "报社"; this.observers = new ArrayList<Observer>(); } @Override public void addObserver(Observer observer) { this.observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = this.observers.indexOf(observer); if (index >= 0) { this.observers.remove(observer); } } @Override void notifyObservers() { if (this.observers.size() > 0) { for (Observer observer : this.observers) { observer.update(super.thing); observer.display(); } } } }
观察者的抽象父类Observer中的代码:
public abstract class Observer { protected String name; protected Object thing; public void update(Object thing) { this.thing = thing; System.out.println(this.name + "收到了消息:" + thing.toString()); } public abstract void display(); }
具体的观察者Person1中的代码:
public class Person1 extends Observer { public Person1() { super.name = "Person1"; } @Override public void display() { System.out.println(super.name + "一边听歌一边看报纸"); } }
具体的观察者还有Person2和Company1,这里以Person1为例,有需要的朋友可以通过博客最后的GitHub地址去GitHub上下载。
测试类Test中的代码:
public class Test { public static void main(String[] args) { // 创建主题(被观察者) Subject office = new NewsOffice(); // 创建观察者 Observer person1 = new Person1(); Observer person2 = new Person2(); Observer company1 = new Compony1(); // 为观察者注册主题 office.addObserver(person1); office.addObserver(person2); office.addObserver(company1); // 主题发送消息 office.setThing("第一条新闻"); System.out.println(); office.setThing("哈哈哈哈哈哈哈哈哈哈"); } }
运行结果如下图所示:
2.2、JAVA内置观察者模式
JAVA内置的观察者模式的代码如下:
被观察者报社类NewsOffice(继承了JAVA内置的被观察者Observable类)的代码如下:
public class NewsOffice extends Observable { public void setNews(String news) { System.out.println("报社发布了新闻:" + news); super.setChanged(); super.notifyObservers(news); } }
自定义的观察者CustomObserver(实现了JAVA内置的观察者接口Observer和自定义的展示接口IDisplay)的代码如下:
// 自定义的展示接口IDisplay中的代码: public interface IDisplay { void display(); } // 自定义的观察者CustomObserver中的代码: public abstract class CustomObserver implements Observer, IDisplay { protected String name; @Override public void update(Observable subject, Object thing) { System.out.println(this.name + "收到了消息:" + thing.toString()); } }
具体观察者Person1中的代码:
public class Person1 extends CustomObserver { public Person1() { super.name = "Person1"; } @Override public void display() { System.out.println(super.name + "一边听歌一边看报纸"); } }
Person2和Company1中的代码同样在GitHub中。
测试类Test中的代码:
public class Test { public static void main(String[] args) { // 创建主题(被观察者) NewsOffice office = new NewsOffice(); // 创建三个观察者 CustomObserver person1 = new Person1(); CustomObserver person2 = new Person2(); CustomObserver company1 = new Company1(); // 为观察者注册主题 office.addObserver(person1); office.addObserver(person2); office.addObserver(company1); // 被观察者发布消息 office.setNews("哈哈哈哈哈哈"); System.out.println(); office.setNews("这不是一条垃圾新闻"); } }
运行结果如下图所示:
最后贴出观察者模式的GitHub代码地址:【GitHub - Observer】。