PS:转载请注明出处
做者: TigerChain
地址: www.jianshu.com/p/b972ba509…
本文出自 TigerChain 简书 人人都会设计模式java
教程简介git
正文github
一、警察抓小偷设计模式
在现实生活中,警察抓小偷是一个典型的观察者模式「这以一个惯犯在街道逛街而后被抓为例子」,这里小偷就是被观察者,各个干警就是观察者,干警时时观察着小偷,当小偷正在偷东西「就给干警发送出一条信号,实际上小偷不可能告诉干警我有偷东西」,干警收到信号,出击抓小偷。这就是一个观察者模式微信
二、装模做样写做业网络
小时候家里家活比较多,爸妈让我去干活的时候,我偶尔会说我要学习「其实不想去干活,固然只是偶尔,我仍是经常干家务的」,而后爸妈就去地里了,我一我的在家里,先摆出一张桌子「上面放好想看的书」,而后打开电视机看起电视剧,可是又怕家里人回到家中看到我在看电视,因而把家里的大门锁住「当爸妈回的时候确定要开门」,当我听见开门声就会立马关掉电视,作到做业桌上「装模做样写做业」----在这过程当中:我充当的就是观察者,爸妈就是被观察者,他们开门就会触发门响「至关于告诉我说他们回来了」,我听到响声「关电视,写做业」,有过相似的经验的朋友们下面点个赞app
三、远程视频会议等ide
老板和员工远程开会:老板是被观察者,员工是观察者。微信公号:微信公号做者是被观察者,微信用户是观察者「当公号做者发送一篇文章,关注了公号的观察者均可以收到文章」等函数
观察者模式的定义布局
观察者模式描述的是一种一对多的关系「一个被观察者对应多个观察者」,当被观察者的状态发生改变时,全部观察者都会获得通知。通俗的理解:观察者模式就是在特定的时刻「被观察者发送通知」干特定的事情「观察者收到通知处理本身相应的事件」
观察者模式的特色
观察者模式的三要素:观察者,被观察者,事件「订阅」
观察者模式的结构
角色 | 类别 | 说明 |
---|---|---|
Subject | 接口或抽象类 | 主题也叫被观察者 |
RealSubject | 真实的主题类 | 具体的被观察者,内部维护了观察者的列表 |
IObserver | 观察者接口或抽象类 | 抽象出观察者的接口 |
RealObserver | 具体的观察者 | 被观察者有更新,观察者立马响应更新 |
观察者模式简单的 UML
在举例以前,咱们先看看一个概念--回调,什么是回调:就调用一圈又回到自已了「通俗的就能够这样认为」
例子一:小明叫爸爸吃饭
举个例子,小明妈妈作好了饭,让小明去地里叫他爸回来吃饭,小明说好的我立刻去,过了半个小时小明和他爸一块儿来了,小明给妈妈的说:“妈,爸回来了”,妈妈说:“好的我知道了,让你爸洗洗手吃饭吧”,在这一过程当中,小明给妈妈的说:“妈,爸回来了”就是一个回调,很差理解?那看代码吧
小明叫爸爸吃饭简单的 UML
写代码
这就是回调,咱们看看的数据的走向 Mom-->xiaoming-->Mom 转了一圈回来了,这就是回调
例子二,模拟Android 中 View 的点击事件
通过例子一,我敢保证多数朋友对回调仍是稀里糊涂,没关系,咱们再来一个例子感觉一下,作过 Android 的朋友必定调用过 View.setOnclickListener(OnClickListener onClickListener) 点击函数,没错 OnClickListener 就是一个回调接口,咱们来使用 java 代码模拟一下这个过程
先看一下 UML
根据 UML 写代码
public class View {
private OnClickListener onClickListener ;
// 触发点击事件
protected void click(){
if(onClickListener !=null){
onClickListener.onClick(this);
}
}
// 设置回调
public void setOnClickListener(OnClickListener onClickListener){
this.onClickListener = onClickListener ;
}
public interface OnClickListener{
// 定义回调方法
void onClick(View v) ;
}
}复制代码
/** * Created by TigerChain * 定义一个按钮 */
public class Button extends View {
public void click(){
super.click();
}
}复制代码
public class Test {
public static void main(String args[]){
Button button = new Button() ;
//看到了没,看到这里是否是很亲切,是否是发现 次哦! 这就是回调
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.print("自定义 View 的回调事件");
}
});
// 模拟用户点击这个运做,Android 系统的 View 是重写手势来调用这个方法的,没有暴露给用户
button.click();
}
}复制代码
若是你看 Android 源码,或是三方的源码会发现好多这样的回调方法,好比网络请求成功失败的回调等。
使用观察者模式实现自定义 View
public class View {
//被观察者的列表
private ArrayList<OnClickListener> onClickListeners = new ArrayList<>() ;
// 触发通知
protected void click(){
for(OnClickListener onClickListener:onClickListeners){
if(onClickListener !=null){
onClickListener.onClick(View.this);
}
}
}
// 注册观察者
public void setOnClickListener(OnClickListener onClickListener){
onClickListeners.add(onClickListener) ;
}
public interface OnClickListener{
// 定义通知的方法
void onClick(View v) ;
}
public void unRegister(OnClickListener onClickListener){
if(onClickListeners.contains(onClickListener)){
onClickListeners.remove(onClickListener) ;
}
}
}复制代码
注意这里的 OnClickListener 就是抽象的观察者
/** * Created by TigerChain */
public class Button extends View {
}复制代码
public class Test {
public static void main(String args[]){
//定义一个被观察者
Button button = new Button() ;
//注册其中一个观察者
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("TigerChain");
}
});
// 注册另外一个观察者
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("never give up");
}
});
// 被观察者触发事件
button.click();
}
}复制代码
PS:总结看到了没,观察者模式和回调是如此的类似,若是咱们把上面的注册观察者改为一个「和 View 回调如出一辙」,能够说回调是一种特殊的观察者模式,回调和观察者联系和区别
有了回调的基础,下面咱们来看看观察者模式的几个精典例子
一、微信公号推送文章
最近看了我文章的人都知道我最近在写关于设计模式这一系列,在这里我「TigerChain」就是一个被观察者,普通的微信用户就是观察者,若是微信用户关注了 TigerChain ,那么我推送的每一篇的文章,微信用户就会第一时间收到个人文章「订阅 TigerChain的用户」,这就是一个典型的观察者模式
微信公号推送文章简单的 UML
根据 UML 撸码
/** * Created by TigerChain * 定义主题「被观察者接口」,全部的公号做者共同属性「其实这里功能上微信系统的功能,直接抽象成被观察者」 */
public interface IWxServerSubject {
// 添加观察者
void attchObserver(IObserver iObserver) ;
// 移除观察者
void detachObserver(IObserver iObserver) ;
// 通知观察者
void notifyObserver() ;
}复制代码
/** * Created by TigerChain * 定义观察者接口,即关注公号的微信用户共同属性 */
public interface Observer {
// 观察者收到信息,内容为 info
void reciveContent(String info) ;
}复制代码
/** * Created by TigerChain * 定义一个真实的被观察者 TigerChain「公号的做者」 * 里面存了订阅 TigerChain 微信公众帐号的读者 */
public class TigerChainSubject implements IWxServerSubject {
// 订阅者列表「观察者列表」,即关注 TigerChain 公号的读者
private List<IObserver> observers = new ArrayList<>() ;
//做者更新公号的内容
private String updateContent ;
@Override
public void attchObserver(IObserver iObserver) {
observers.add(iObserver) ;
}
@Override
public void detachObserver(IObserver iObserver) {
if(observers.contains(iObserver)) {
observers.remove(iObserver);
}
}
@Override
public void notifyObserver() {
for (IObserver iObserver:observers) {
iObserver.reciveContent(updateContent);
}
}
/** * 是否关注个人公号 * @param iObserver * @return */
public boolean isAttchObserver(IObserver iObserver){
return observers.contains(iObserver) ;
}
/** * TigerChain 在公号中发布文章 * @param updateContent */
public void submitContent(String updateContent){
this.updateContent = updateContent ;
this.notifyObserver();
}
}复制代码
/** * Created by TigerChain * 微信用户 */
public class ReaderObserver implements Observer {
// 微信用户的姓名
private String name ;
public ReaderObserver(String name){
this.uname = name ;
}
@Override
public void reciveContent(String info) {
System.out.println(uname+"注意,TigerChain 发送了文章---"+info);
}
public String getUname(){
return this.name ;
}
}复制代码
能够看到微信用户有接收推送文章的能力「前提是要关注公号做者」
/** * Created by TigerChain * 测试类 */
public class Test {
public static void main(String args[]){
IWxServerSubject iWxServerSubject = new TigerChainSubject() ;
// 微信用户
ReaderObserver zhangsai = new ReaderObserver("张三") ;
ReaderObserver lisi = new ReaderObserver("李四") ;
ReaderObserver wangwu = new ReaderObserver("王五") ;
ReaderObserver zhaoLiu = new ReaderObserver("赵六") ;
// 微信用户张三关注个人公号「即订阅」
iWxServerSubject.attchObserver(zhangsai);
// 微信用户李四关注个人公号「即订阅」
iWxServerSubject.attchObserver(lisi);
// 微信用户王五关注个人公号「即订阅」
iWxServerSubject.attchObserver(wangle);
// 我「被观察者」发布了一篇文章--观察者模式
((TigerChainSubject)iWxServerSubject).submitContent("人人都会设计模式:观察者模式") ;
boolean isAttch = ((TigerChainSubject)iWxServerSubject).isAttchObserver(zhaoLiu) ;
if(!isAttch){
System.out.println(zhaoLiu.getUname()+"你好!你尚未关注 TigerChain ,请关注先,谢谢");
}
}
}复制代码
咱们看到和现实状况同样,普通微信用户关注公号做者,而后做者发送文章,用户就能够收到文章了
二、狼王开会
话说冬天来了,狼得找过冬的食物,狼王组织如开了紧急会议,下面的群狼都看着狼王传递会议精神和安排任务,此时狼王就是被观察者,群狼就是观察者,咱们来看看 UML
狼王开会简单的 UML
根据 UML 撸码
/** * Created by TigerChain * 抽象的被观察者 */
public interface Wolf {
// 添加观察者
void attchObserver(NormalWolf observer) ;
// 移除观察者
void detchObserver(NormalWolf observer) ;
// 通知观察者
void notifyObserver(String str) ;
}复制代码
/** * Created by 抽象的观察者,普通的狼 */
public abstract class NormalWolf {
// 拿到被观察者的引用
protected IWolf iWolf ;
/** * 收到狼王下达的命令 * @param str */
public abstract void reciveCommand(String str) ;
}复制代码
因为一个狼群中只有一个狼王,因此狼王是一个单例
/** * Created by TigerChain * 狼王「被观察者,下面的狼都看狼王的眼色行事」,是一个单例模式 */
public class LangWang implements Wolf{
private static LangWang instance ;
private LangWang(){}
public static LangWang getInstance(){
if(instance == null){
synchronized (LangWang.class){
if(instance == null){
instance = new LangWang() ;
}
}
}
return instance ;
}
// 除过狼王外的狼「观察者」
private List<NormalWolf> observers = new ArrayList<>() ;
// 狼王下达的命令
private String mingLing ;
@Override
public void attchObserver(NormalWolf observer) {
observers.add(observer);
}
@Override
public void detchObserver(NormalWolf observer) {
if(observers.contains(observer)){
observers.remove(observer) ;
}
}
@Override
public void notifyObserver(String str) {
for(NormalWolf observer:observers){
observer.reciveCommand(str);
}
}
/** * 下达命令 * @param mingLing */
public void xiaDaMingling(String mingLing){
this.mingLing = mingLing ;
this.notifyObserver(mingLing);
}
}复制代码
/** * Created by TigerChain * 侦查狼,另外一个观察者 */
public class ZhenChaLang extends NormalWolf {
public ZhenChaLang(IWolf iWolf){
this.iWolf = iWolf ;
this.iWolf.attchObserver(this);
}
@Override
public void reciveCommand(String string) {
System.out.println("侦查狼:狼王开会传递的信息是 \n"+string);
}
}复制代码
在这里咱们实例化一个侦查狼的时候就会把它注册到被观察者中,也就是狼王开会的时候,群狼确定狼群中的一员「外来狼可不行」,只有内部狼「内部会员」才有资格开会「这种关系就至关于注册这个过程」
/** * Created by TigerChain * 捕猎狼---观察者 */
public class BuLieLang extends NormalWolf {
public BuLieLang(IWolf iWolf){
this.iWolf = iWolf ;
// 添加观察者,即捕猎狼放在狼王组织中
this.iWolf.attchObserver(this);
}
@Override
public void reciveCommand(String string) {
System.out.println("捕猎狼:狼王开会传递的信息是 \n"+string+"\n");
}
}复制代码
/** * Created by TigerChain * 测试类 */
public class Test {
public static void main(String args[]){
// 使用单例模式
LangWang langWang = LangWang.getInstance() ;
BuLieLang buLieLang = new BuLieLang(langWang) ;
ZhenChaLang zhenChaLang = new ZhenChaLang(langWang) ;
// 狼王下达命令就是发送通知
langWang.xiaDaMingling("一、分工合做,捕猎狼根据侦查狼反馈看机行事 \n" +
"二、侦查狼永远把危险放在第一位,遇到危险第一时间提醒你们撤退");
}
}复制代码
狼王下达命令就是发送通知,那么现场中的狼都会收到通知,典型的观察者模式
三、自定义 EventBus
在 Android 中咱们经常使用 EventBus,它至关因而一个单例广播,咱们来自定义一个简单的 EventBus 「不考虑线程切换」,其实它也是一种观察者模式「俗称发布、订阅模式」
自定义 EventBus 简单的 UML
代码这里不贴了,我已经上传到 github 上了,你们能够自行看看:github.com/githubchen0…
一、RecyclerView 中使用观察者模式
RecyclerView 中观察者模式简单的 UML
源码就不分析了「贴出代码估计又得一篇来讲」,给出下面流程,你们自行看一下就明白了,动动手印象更深
从 setAdapter 开始看一下观察者流程
二、ViewTreeObserver
ViewTreeObserver 是用来监听视图树的观察者,若是视图树发生全局改变的时候就会收到通知
其中,被观察者是 ViewTree ,观察者是 ViewTreeObserver
抽取 ViewTreeObserver 部分代码讲解
这里说说 view.getViewTreeObserver().addOnGlobalLayoutListener(xxx) 场景,其它的雷同
public final class ViewTreeObserver {
...
public interface OnGlobalLayoutListener {
public void onGlobalLayout();
}
...
// 添加监听器
public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
}
mOnGlobalLayoutListeners.add(listener);
}
}
...
// 分发事件至关于发送通知,即被观察者调用--View
public final void dispatchOnGlobalLayout() {
final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
if (listeners != null && listeners.size() > 0) {
CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
try {
int count = access.size();
for (int i = 0; i < count; i++) {
access.get(i).onGlobalLayout();
}
} finally {
listeners.end();
}
}
}复制代码
如今咱们有了观察者 ViewTreeObserver ,观察者是 ViewTree 咱们说了,主要问题的就是 dispatchOnGlobalLayout 谁调用了,只有触发了这个方法那么事件就回调回来了「这个方法确定是被观察者调用了,系统调用的」,方法在 ViewRootImpl「关于 ViewRootImpl 可自行去查看,不在本篇的范围」 中体现出来了
看看 ViewRootImpl 的部分代码
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
...
private void performTraversals(){
...
// 执行测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
// 执行布局
performLayout(lp, mWidth, mHeight);
...
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
// 注意看这里,这里触发了 dispatchOnGlobalLayout 方法,系统调用
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
...
// 执行绘制
performDraw();
...
}
...
}复制代码
看到了没,mAttachInfo.mTreeObserver.dispatchOnGlobalLayout() 方法是在 ViewRootImpl 中调用了「便是 View 调用了,只要 View 树而已发生改变,就会调用」,是由系统调用的「View 的布局完成这后,就会调用」,而且还调用了自定义 View 的测量,布局,绘制方法。
使用场景:好比咱们想在 Activity 的 onCreate() 方法中取得某个 View 的宽高,此时是取不到的,因为布局尚未完成加载以前取到的是 0 ,因此使用 view.getViewTreeObserver().addOnGlobalLayoutListener(xxx) 里面就能够获取到 view 的宽高了,demo 代码以下
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {// 当layout执行结束后回调
//使用完必须撤销监听(只测量一次),不然,会一直不停的不定时的测量,这比较耗性能
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);//Added in API level 16
//view.getViewTreeObserver().removeGlobalOnLayoutListener(this);//废弃了
int width = view.getMeasuredWidth();
int width2 = view.getWidth();//和上面的值是同样的
}
});复制代码
三、ListView
ListView 中使用观察者模式和 RecyclerView 相似,你们能够扒扒这部分源码,这里就不说了
优势
缺点
到此为止,咱们的观察者模式就说完了,必定要扒扒 Android 源码中相应的观察者模式,你会有一种恍然大悟的感受,点赞是一美德
之后文章会第一时间发在公号,请你们添加博文的公号,扫描添加便可关注
公众号:TigerChain![]()
TigerChain