设计模式之观察者模式
部分内容参考其他博主,定义的内容从未改变……
一、观察者模式介绍
观察者模式是对象的行为模式,又叫做发布——订阅模式,它有个一个重要的作用就是解耦,将观察者与被观察者解耦,使得它们之间依赖性更小,不依赖于实体类。
二、观察者模式定义
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题在状态上发生改变的时候,就会通知所有的拥有该主题对象的观察者,使得它们能够自动的更新自己
三、观察者模式使用场景
①对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
②对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
③事件多级触发场景。
四、观察者模式UML类图
所有涉及到的角色:
①抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集中(比如说ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象的主题接口又提供了一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
②具体主题(ConcreteSubject)角色:将有关的状态保存在具体的被观察者对象里,在具体主题的内部状态发生改变的时候,将所有拥有的观察者全部的通知一遍。具体主题角色又叫做具体被观察者(ConcreteObservable)角色。
③抽象观察者(Observer)角色:为所有的观察者定义一个接口,在得到主题的通知的时候更新自己,这个接口又叫做更新接口。
④具体观察者(ConcreteObserver)角色:存储与主题状态自洽(不会相互矛盾)的状态。具体观察者角色抽象观察者角色所要求更新的接口,以便使自身的状态与主题相协调。如果需要,具体观察者角色可以保持一个指向主题对象的引用(就是更新方法中的参数是抽象主题对象)。
五、代码实现(例子)
//抽象主题
public abstract class Subject {
//内容是否改变的标识
private boolean changed = false;
private List<IWatcher> watcherList = new ArrayList<>();
// 表示主题内容发生改变
public void setChanged() {
changed = true;
}
// 添加观察者
public void addWatcher(IWatcher watcher) {
watcherList.add(watcher);
}
// 移除观察者
public void deleteWatcher(IWatcher watcher) {
watcherList.remove(watcher);
}
// 通知观察者
public void notifyWatcher(String msg) {
if (!changed) {
return;
}
changed = false;
for (int i = 0; i < watcherList.size(); i++) {
watcherList.get(i).update(obj);
}
}
}
//具体抽象主题
public class WeatherSubject extends Subject {
public String message;
public void postWeather(String message) {
if (this.message == message) {
return;
}
this.message = message;
// 表示状态或者内容发生了改变
setChanged();
// 通知所有的观察者
notifyWatcher(message);
}
}
//抽象观察者
public interface IWatcher {
void update(String msg);
}
//具体观察者
public class Subscriber implements IWatcher{
public String name;
public Subscriber(String name) {
this.name = name;
}
@Override
public void update(String msg) {
System.out.println("Hi," + name + ",天气更新了," + msg);
}
}
public class PushTest {
public static void main(String[] args) {
// 被观察者
WeatherSubject weather = new WeatherSubject();
// 观察者
Subscriber subscriberA = new Subscriber("订阅者A");
Subscriber subscriberB = new Subscriber("订阅者B");
Subscriber subscriberC = new Subscriber("订阅者C");
Subscriber subscriberD = new Subscriber("订阅者D");
// 将观察者注册到被观察者列表中
weather.addWatcher(subscriberA);
weather.addWatcher(subscriberB);
weather.addWatcher(subscriberC);
weather.addWatcher(subscriberD);
// 被观察者发布消息
weather.postWeather("今天天气9°,请注意保暖!");
}
}
测试结果(通知了所有的订阅主题对象的观察者):
然而在观察者模式中,又分为退模型和拉模型两种形式。
推模型:主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
拉模型:主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
拉模型例子:
//抽象主题
public abstract class Subject {
//内容是否改变的标识
private boolean changed = false;
private List<IWatcher> watcherList = new ArrayList<>();
// 表示主题内容发生改变
public void setChanged() {
changed = true;
}
// 添加观察者
public void addWatcher(IWatcher watcher) {
watcherList.add(watcher);
}
// 移除观察者
public void deleteWatcher(IWatcher watcher) {
watcherList.remove(watcher);
}
// 通知观察者
public void notifyWatcher(String msg) {
if (!changed) {
return;
}
changed = false;
for (int i = 0; i < watcherList.size(); i++) {
watcherList.get(i).update(this);
}
}
}
//具体抽象主题
public class WeatherSubject extends Subject {
public String message;
public void postWeather(String message) {
if (this.message == message) {
return;
}
this.message = message;
// 表示状态或者内容发生了改变
setChanged();
// 通知所有的观察者
notifyWatcher(this);
}
}
//抽象观察者
public interface IWatcher {
void update(Subject subject);
}
//具体观察者
public class Subscriber implements IWatcher{
public String name;
public Subscriber(String name) {
this.name = name;
}
@Override
public void update(String msg) {
@Override
public void update(Subject subject) {
System.out.println("Hi," + name + ",天气更新了--->"
+ ((WeatherSubject) subject).message);
}}
}
public class PullTest {
public static void main(String[] args) {
// 被观察者
WeatherSubject weather = new WeatherSubject();
// 观察者
Subscriber subscriberA = new Subscriber("订阅者A");
Subscriber subscriberB = new Subscriber("订阅者B");
Subscriber subscriberC = new Subscriber("订阅者C");
Subscriber subscriberD = new Subscriber("订阅者D");
// 将观察者注册到被观察者列表中
weather.addWatcher(subscriberA);
weather.addWatcher(subscriberB);
weather.addWatcher(subscriberC);
weather.addWatcher(subscriberD);
// 被观察者发布消息
weather.postWeather("今天天气9°,请注意保暖!");
}
}
代码结果就没差了,这里就不粘贴了(博主比较懒)……
现在,我们来看一下如何使用java Util包下已经写好的抽象主题类和观察者接口吧
//程序猿实现一个观察者接口(Util包下的Observer)
public class Coder implements Observer {
public String name;
public Coder(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println("Hi," + name + ",News更新了,内容:" + arg);
}
}
//新闻主题继承至被观察者接口(Util包下的Observable)
public class News extends Observable {
public void postMessage(String content) {
setChanged();
notifyObservers(content);
}
}
//
public class Test {
public static void main(String[] args) {
News news = new News();
//观察者
Coder coder = new Coder("程序员A");
Coder coder2 = new Coder("程序员C");
Coder coder3 = new Coder("程序员B");
Coder coder4 = new Coder("程序员D");
//将观察者注册到被观察者列表中
news.addObserver(coder);
news.addObserver(coder2);
news.addObserver(coder3);
news.addObserver(coder4);
//被观察者发布消息
news.postMessage("新闻更新了!");
}
}
能猜到结果吧(对,就是脑子里那个),不粘贴了……
六、总结
不说话,悄悄总结一波:
优点:
◆观察者和被观察者之间是抽象耦合;
◆增强系统灵活性、可扩展性。
缺点:
◆如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
◆如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。
◆虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
观察者模式的主要作用就是对象解耦,将观察者与被观察者完全隔离,只依赖于Observer和Observable抽象。
来一波Android小提示:
adapter.notifydatasetchanged()就是有运用了观察者模式,以及四大组件之一的BroadcastReceiver也用到了观察者模式。
这是楼主第一波写文章,勿喷勿闹稳稳上分(PS:大神求带,第一次写忘记改标题了,又重新提交一遍…)