是什么(what)
观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subcribe Design Pattern)。是23种设计模式之一,属于行为型模式。
概念:
英文:Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and update automatically。
中文:定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。
以微信公众号订阅为背景,介绍观察者模式:
如上图所示,服务号就是我们的主题,使用者就是观察者。现在我们明确下功能:
1、服务号就是主题,业务就是推送消息
2、观察者只需要订阅主题,只要有新的消息就会送来
3、当不想要此主题消息时,取消订阅
4、只要服务号还在,就会一直有人订阅
好了,现在我们来看看观察者模式的类图(取自Head First 设计模式一书):
怎么用(how)
业务场景
1.上游一来数据,下游就变化
生活场景
1.接入气象站天气数据,天气变化,多种布告板(大屏幕等)立即显示变化(Head First 设计模式例子)
2.少林足球,星爷在台上表演,台下观众根据台上表演做出变化(一群人看着一个东西而做出相应的反应。)
3.猫抓老鼠,老鼠一出来,猫就扑上去
4.烧水的同时看《三国演义》,水烧开,关闭开关,洗脚
…
现有框架及JDK使用
1.Java
Observer接口和Observable类
EventObject和EventListener
2.Spring
EventObject和EventListener
参考文章:
https://blog.csdn.net/shang_0122/article/details/120679734
代码show
我们模拟一个《逻辑思维》服务号,和一些订阅者。
首先开始写我们的主题接口,和观察者接口:
package cn.wangzengqiang.study.designpattern.observer.own;
/**
* 主题接口,所有的主题必须实现此接口
*/
public interface Subject {
/**
* 注册一个观察者
*
* @param observer
*/
void registerObserver(Observer observer);
/**
* 移除一个观察者
*
* @param observer
*/
void removeObserver(Observer observer);
/**
* 通知所有的观察着
*/
void notifyObservers();
}
package cn.wangzengqiang.study.designpattern.observer.own;
/**
* 所有的观察者需要实现此接口
*/
public interface Observer {
void update(String msg);
}
接下来《逻辑思维》服务号的实现类:
package cn.wangzengqiang.study.designpattern.observer.own;
import java.util.ArrayList;
import java.util.List;
/**
* 逻辑思维 订阅号
*/
public class LogicalThinking implements Subject {
private List<Observer> observers = new ArrayList<Observer>();
/**
* 逻辑思维每日60s主题
*/
private String msg;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int i = observers.indexOf(observer);
if (i >= 0) {
observers.remove(i);
}
}
/**
* 主题更新消息
*/
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(msg);
}
}
public void setMsg(String msg) {
this.msg = msg;
notifyObservers();
}
}
模拟两个使用者:
package cn.wangzengqiang.study.designpattern.observer.own;
/**
* 订阅者:工一
*/
public class Gongyi implements Observer {
private Subject subject;
public Gongyi(Subject subject) {
this.subject = subject;
subject.registerObserver(this);
}
@Override
public void update(String msg) {
System.out.println(getClass().getSimpleName() + ",请您收看罗胖60s:" + msg);
}
}
package cn.wangzengqiang.study.designpattern.observer.own;
/**
* 订阅者:木子
*/
public class Muzi implements Observer {
private Subject subject;
public Muzi(Subject subject) {
this.subject = subject;
subject.registerObserver(this);
}
@Override
public void update(String msg) {
System.out.println(getClass().getSimpleName() + ",请您收看罗胖60s:" + msg);
}
}
可以看出:服务号中维护了所有向它订阅消息的使用者,当服务号有新消息时,通知所有的使用者。
整个架构是一种松耦合,主题的实现不依赖与使用者,当增加新的使用者时,主题的代码不需要改变;使用者如何处理得到的数据与主题无关;
最后看下测试代码:
package cn.wangzengqiang.study.designpattern.observer.own;
import org.junit.Test;
public class LogicalThinkingTest {
@Test
public void test1() {
//模拟一个《逻辑思维》的服务号
LogicalThinking logicalThinking = new LogicalThinking();
//订阅者:工一
Observer gongyi = new Gongyi(logicalThinking);
Observer muzi = new Muzi(logicalThinking);
//发布消息
logicalThinking.setMsg("2022.12.20 怎么坚持一件长期的事");
logicalThinking.setMsg("2022.12.21 10年期满,今日告别");
}
}
输出结果:
Gongyi,请您收看罗胖60s:2022.12.20 怎么坚持一件长期的事
Muzi,请您收看罗胖60s:2022.12.20 怎么坚持一件长期的事
Gongyi,请您收看罗胖60s:2022.12.21 10年期满,今日告别
Muzi,请您收看罗胖60s:2022.12.21 10年期满,今日告别
类图:
恭喜你学会了观察者模式,上面的观察者模式使我们从无到有的写出,当然了java中已经帮我们实现了观察者模式,借助于java.util.Observable和java.util.Observer。
下面我们使用Java内置的类实现观察者模式:【以我订阅的 逻辑思维 和 人民日报 公众号 为例】
首先是一个 逻辑思维 的订阅号主题:
package cn.wangzengqiang.study.designpattern.observer.own.java;
import java.util.Observable;
/**
* 逻辑思维 订阅号
*/
public class LogicalThinking extends Observable {
private String msg;
public String getMsg() {
return msg;
}
/**
* 主题更新消息
*
* @param msg
*/
public void setMsg(String msg) {
this.msg = msg;
setChanged();
notifyObservers();
}
}
下面是一个 人民日报 的订阅号主题:
package cn.wangzengqiang.study.designpattern.observer.own.java;
import java.util.Observable;
/**
* 人民日报 订阅号
*/
public class PeopleDaily extends Observable {
private String msg;
public String getMsg() {
return msg;
}
/**
* 主题更新消息
*
* @param msg
*/
public void setMsg(String msg) {
this.msg = msg;
setChanged();
notifyObservers();
}
}
最后是我们的使用者:
package cn.wangzengqiang.study.designpattern.observer.own.java;
import java.util.Observable;
import java.util.Observer;
/**
* 订阅者:工一
*/
public class Gongyi implements Observer {
public void registerSubject(Observable observable) {
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof LogicalThinking) {
LogicalThinking logicalThinking = (LogicalThinking) o;
System.out.println(logicalThinking.getClass().getSimpleName() + "'s msg -- >" + logicalThinking.getMsg());
}
if (o instanceof PeopleDaily) {
PeopleDaily peopleDaily = (PeopleDaily) o;
System.out.println(peopleDaily.getClass().getSimpleName() + "'s msg -- >" + peopleDaily.getMsg());
}
}
}
测试代码:
package cn.wangzengqiang.study.designpattern.observer.own.java;
import org.junit.Test;
public class LogicalThinkingTest {
/**
* 测试java内置的观察者模式实现
*/
@Test
public void test1() {
LogicalThinking logicalThinking = new LogicalThinking();
PeopleDaily peopleDaily = new PeopleDaily();
Gongyi gongyi = new Gongyi();
//工一 订阅 逻辑思维,人民日报
gongyi.registerSubject(logicalThinking);
gongyi.registerSubject(peopleDaily);
//发布消息
logicalThinking.setMsg("20230102 收藏|罗胖60秒十年精华版:那些打开思路的概念");
peopleDaily.setMsg("20230102 来了!新闻早班车");
}
}
输出结果:
LogicalThinking's msg -- >20230102 收藏|罗胖60秒十年精华版:那些打开思路的概念
PeopleDaily's msg -- >20230102 来了!新闻早班车
类图:
可以看出,使用Java内置的类实现观察者模式,代码非常简洁,对addObserver,removeObserver,notifyObservers都已经为我们实现了,
可以看出Observable(主题)是一个类,而不是一个接口,基本上书上都对于Java的如此设计抱有反面的态度,觉得Java内置的观察者模式,违反了面向接口编程这个原则,但是如果转念想一想,的确你拿一个主题在这写观察者模式(我们自己的实现),接口的思想很好,但是如果现在继续添加很多个主题,每个主题的addObserver,removeObserver,notifyObservers代码基本都是相同的吧,接口是无法实现代码复用(Java8接口默认实现可以)的,而且也没有办法使用组合的模式实现这三个方法的复用,所以我觉得这里把这三个方法在类中实现是合理的。
为什么用(why)
参考设计模式之美的一段总结
回到本质,设计模式要干的事情就是解耦。
创建型模式是将创建对象和使用对象解耦,
结构型模式是将不同功能代码解耦,
行为型模式是将不同的行为代码解耦,
具体到观察者模式,是将观察者和被观察者代码解耦。