社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
观测者模式常用于实现订阅功能的场景,比如说微博的订阅,消息订阅等。我们订阅了它们,它们在有更新的时候就会给我们推送它们的更新信息,这样我们就不用每次都要去刷新它的状态了。观察者模式它定义了一种一对多的依赖关系,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。假如一个对象的状态更新,需要其他对象也同步更新的时候,而且这些其他对象可以动态改变,这个时候我们的观测者模式就派上用场类。
我们看看类图(图片来自《Head First 设计模式》)
下面以我们日常订报纸为例,写个demo来更加细化地了解观察者模式是怎么回事。
报纸的订阅很简单,报社出版报纸,每天更新报纸的内容,我们订阅了报纸,就会每天有人送报纸过来(当然你也可以自己去拿,这涉及到消息的推送方式,是你给我,还是我自己拿,这一会再讨论),从报社订报纸的人有人多,取消订阅的人也很多,因此订阅者是可以动态变化的。折射到我们观察者模式,出版报纸的报社就称为”Subject”主题,订阅报纸的人则称为”Observer”观察者。 根据上面的类图,我们开始实现这一订阅报纸的流程。
1.先建立接口
package Observer;
/**
* Created by gray on 2017/8/13.
*/
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
package Observer;
/**
* Created by gray on 2017/8/13.
*/
public interface Observer {
public void update(String context);
}
2.实现接口
对于报社我们要实现的是Subject接口
package Observer;
import java.util.ArrayList;
import java.util.List;
/**
* Created by gray on 2017/8/13.
*/
public class Newspaper implements Subject {
private List<Observer> list;
private String context;
public Newspaper() {
list = new ArrayList<>();
}
@Override
public void registerObserver(Observer o) {
list.add(o);
}
@Override
public void removeObserver(Observer o) {
int i = list.indexOf(o);
if (i >= 0) {
list.remove(i);
}
}
@Override
public void notifyObserver() {
if (list!=null && list.size()>0){
for (Observer o : list){
o.update(this.context);
}
}
}
public void updateContext(String context){
this.context = context;
notifyObserver(); // 有更新就通知
}
}
对于订阅者我们实现Observer接口
package Observer;
/**
* Created by gray on 2017/8/13.
*/
public class Subscriber implements Observer {
private String context;
private Subject subject;
public Subscriber(Subject subject){
this.subject = subject;
subject.registerObserver(this);
}
@Override
public void update(String context) {
this.context = context;
System.out.println("这里是订阅者1,今天收到的报纸内容是:"+context);
}
}
好了,现在来做个简单的测试吧
package Observer;
/**
* Created by gray on 2017/8/13.
*/
public class Test {
public static void main(String args[]){
Newspaper n = new Newspaper();
Subscriber s = new Subscriber(n);
String str1 = "今天是8月1号,发生了xxxx事情...";
n.updateContext(str1);
String str2 = "今天是8月2号,发生了XXXX事情...";
n.updateContext(str2);
String str3 = "今天是8月3号,发生了XXXXXXX事情...";
n.removeObserver(s); //取消订阅
n.updateContext(str3);
String str4 = "今天是8月4号,发生了xxxxxx事情...";
n.registerObserver(s); //订阅
n.updateContext(str4);
}
}
上面的测试程序中,3号的报纸我取消类订阅,4号的报纸我又重新订阅,执行结果如图所示:
这样,简单的观察者模式就这样实现了。
这样,上面所展示的观察者模式是主动给我们推数据,那我们要自己拉数据该怎么做?我们看看Java API内置的观测者模式是怎么运作的。我们对以上例子做个简单的修改。
1.实现SUbject接口改成继承java.util.Observable类
package Observer;
import java.util.Observable;
/**
* Created by gray on 2017/8/13.
*/
public class Newspaper2 extends Observable {
private String context;
public Newspaper2(){}
public void updateContext(String context){
this.context = context;
setChanged(); // 通知前调用指示状态发生了改变
notifyObservers();
}
public String getContext() {
return context;
}
}
订阅者实现java.util.Observer接口
package Observer;
import java.util.Observable;
import java.util.Observer;
/**
* Created by gray on 2017/8/13.
*/
public class Subscriber2 implements Observer {
private Observable observable;
private String context;
public Subscriber2(Observable observable){
this.observable = observable;
this.observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof Newspaper2) {
this.context = ((Newspaper2) o).getContext(); // 这里采用的拉的方式
System.out.println("这里是订阅者1,今天收到的报纸内容是:"+ this.context);
}
}
public void removeSubscriber(){
this.observable.deleteObserver(this);
}
public void subscriber(){
this.observable.addObserver(this);
}
}
接下来我们做第二个测试,测试代码修改如下:
Newspaper2 n = new Newspaper2();
Subscriber2 s = new Subscriber2(n);
String str1 = "今天是8月1号,发生了xxxx事情...";
n.updateContext(str1);
String str2 = "今天是8月2号,发生了XXXX事情...";
n.updateContext(str2);
String str3 = "今天是8月3号,发生了XXXXXXX事情...";
s.removeSubscriber(); //取消订阅
n.updateContext(str3);
String str4 = "今天是8月4号,发生了xxxxxx事情...";
s.subscriber(); //订阅
n.updateContext(str4);
测试结果与第一种一样
总结一下:java内置的API所实现的观察者模式有一个坏处首先Observable它是个类,继承总没有实现接口复用性好。反正萝卜青菜各有所爱,无论是哪种实现方式,也无论是推的方式还是拉的方式,取决于你的应用场景。个人觉得如果你要的数据很复杂,那就采用推的方式,如果简单那就推的拉的都可以。
以上就是我对《Head First 设计模式》中关于观察者模式的一些理解及参考书上的例子自己写的一些例子。记录一下方便今后复习。详细代码见github
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!