glmapper

菜鸟成长系列-观察者模式

字数统计: 2.2k阅读时长: 8 min
2018/11/10 Share

最近想深入研究下响应式编程,作为基础很有必要来把观察者模式撸一遍;一开始我是觉得很easy,然后就直接开撸了,撸着撸着发现撸不动了。因为我突然不太明白这个模式了,说好的观察者,到底发布-订阅的两者执行者谁才是观察者?又或者说还有其他角色?但是根据《JAVA与模式》一书中的结构,并没有额外的角色出现。

思考中….,好吧想不出来….,跑步去…

跑步时我给自己罗列了几个问题:

这里先抛出定义:GOF给观察者模式如下定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

  • 既然是对象状态发生变更,那么到底是谁的状态发生了变更,又导致了谁被通知。
  • 观察者模式既然又可以称之为“发布-订阅模式”,那么对应起来,观察者到底承当了“发布”的角色还是“订阅”的角色。就是说观察者到底是主动的还是被动的?
  • 被观察者又干了什么事?它是主动的还是被动的角色?

这里由于一些定式思维,总会觉得既然是“被观察者”,那么这个“被”字就是不是就表明“被观察者”是被动接受变更的一方,也就是接受通知的一方呢?

之前我也是走到这个胡同里了,程序写完总觉得哪里不对;回过头看,还是自己太年轻,没有get到哪些大佬们的点。

先来看程序;这里用掘金来打个比方,我的博客glmmaper作为被观察者,也就是发布者。掘金小伙伴们作为观察者,也就是订阅者。

具体逻辑:小伙伴们(订阅者)关注(订阅)了我的博客(发布者),如果我发布了一篇文章(状态变更),就会通知(推送消息)所有关注我的小伙伴。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.glmapper.designmode.observor;
/**
* @description: 抽象主题接口
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public interface Subject {
/**
* 新增关注者
* @param observer 关注的小伙伴
*/
void addFocusObserver(Observer observer);

/**
* 取消关注
* @param observer 取消关注的小伙伴
*/
void removeFocusObserver(Observer observer);

/**
* 通知机制,通知机制由相关事件来触发,比如说发布文章
* @param blogName 博客名
* @param articleName 文章名
*/
void notifyObservers(String blogName,String articleName);
}

三个方法,一个是博客主页增加了一个关注者;一个是博客主页有小伙伴取消的关注(对于博客来说就是移除一个关注者,这里不知道是否也会觉得别扭?明明你取消的关注,为啥说成是我移除你,也就是不让你关注了,还能这么玩?这里肯定是需要在引入其他的一些辅助机制,比如说你在客户端发起了一个取消关注的请求,后端处理的时候掘金的工程师们就是在我的关注列表中将你移除的,嗯,这么一想确实是我不让你关注了。😄….);最后一个方法是发起一个通知。下面是一个具体的博客,比如说是glmapper;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.glmapper.designmode.observor;

import java.util.ArrayList;
import java.util.List;

/**
* @description: 这个是具体发布者,这里比喻成我的博客glmapper
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public class ConcreteSubject implements Subject {
/** 我的当前关注列表 */
List<Observer> Observers = new ArrayList<>();
/** 我的博客名 :求关注 */
private static final String blogName = "glmapper";

@Override
public void addFocusObserver(Observer observer) {
Observers.add(observer);
}

@Override
public void removeFocusObserver(Observer observer) {
Observers.remove(observer);
}

@Override
public void notifyObservers(String blogName,String articleName) {
for (Observer observer:Observers) {
observer.update(blogName,articleName);
}
}

/**
* 这里是发布文章,触发通知事件
*/
public void publishArticle(String articleName){
notifyObservers(blogName,articleName);
}
}

前面提到,通知事件肯定是由于某些状态发生变更了,才会进行通知,这里就可以比方为我发布了一篇博客,然后通知你(这里只能假如你关注了)。再来看观察者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.glmapper.designmode.observor;

/**
* @description: 订阅者抽象接口
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public interface Observer {
/**
* 调用此方法会更新状态,做出相应的动作
* @param blogName
* @param articleName
*/
void update(String blogName,String articleName);
}

抽象订阅者,有一个update方法,通知你去做出相应的动作,具体动作每个观察者都可能不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.glmapper.designmode.observor;

/**
* @description: 这个是具体订阅者,这里可以比喻成博客关注者,
* 收到变更信息之后需要做出相应的动作
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public class ConcreteObserver implements Observer {

@Override
public void update(String blogName,String articleName) {
System.out.println(blogName+"发布了新的文章,文章名为:"+articleName);
read(articleName);
}

private void read(String articleName){
System.out.println("即将阅读 "+articleName+" 这篇文章");
}
}

上面是一个具体的关注者,加入说就是你。博客更新之后发了一个通知给你(掘金app推送的消息),然后你点了一下,这个也是一种动作。例子中举的是read,就是关注者做出阅读的动作。

看下最后的运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.glmapper.designmode.observor;

/**
* @description: [描述文本]
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public class MyMainIndex{

public static void main(String[] args) {
//博客主体
ConcreteSubject subject = new ConcreteSubject();
//关注者:handSome是帅气的意思
Observer handSome = new ConcreteObserver();
//增加一个关注者
subject.addFocusObserver(handSome);
//发一篇文章
subject.publishArticle("设计模式-观察者模式");
}

}


glmapper发布了新的文章,文章名为:设计模式-观察者模式
即将阅读 设计模式-观察者模式 这篇文章

酒桶说:啊,欢乐时光总是短暂的

所以作为积累,还是需要将一些基本的概念来罗列一下的。

观察者模式类图

主要角色:

  • 抽象主题角色(Subject:主题角色将所有对观察者对象的引用保存在一个集合中,每个主题可以有任意多个观察者。抽象主题提供了增加和删除等观察者对象的接口。
  • 抽象观察者角色(Observer):为所有的具体观察者定义一个接口,在观察的主题发生改变时更新自己。
  • 具体主题角色(ConcreteSubject)(1个):存储相关状态到具体观察者对象,当具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
  • 具体观察者角色(ConcretedObserver)(多个):存储一个具体主题对象,存储相关状态,实现抽象观察者角色所要求的更新接口,以使得其自身状态和主题的状态保持一致。

具体关系:

  • 抽象主题(Subject)(接口)–>被具体主题(ConcreteSubject)角色(1个)实现

  • 抽象观察者(Observer)(接口)–>被具体观察者(ConcretedObserver)角色(N个)实现

  • 观察者对象载入主题方法,并在主题方法中调用观察者对象实现的接口方法update来让自己发生变更响应。

一些场景:

  • 当对一个对象的的改动会引发其他对象的变动时,而且你无法预测有多少个对象需要被改动。
  • 当一个对象需要有能力通知其他对象,且不需要了解这些对象是什么类型时。

基于发布订阅的具体实现例子还是很多的,比较典型的就是这种订阅一个博客,然后博客更新推送;还有微信公众号,服务号这些。

到这里我们再回过头来看一开始留下的几个问题:

  • 被观察者的状态发生变更,然后“主动通知”观察者,并不是说,观察者主动去获取通知。
  • 被观察者是消息发布者,观察者是消息订阅者;观察者是被动接受者。
  • 被观察者的作用就是存储当前的观察者列表,然后提供一些通知机制来告诉观察者自己发生了状态变更,是主动者。

OK,观察者模式就撸到这里,也欢迎小伙伴们提出自己珍贵的意见;有写的不当之处烦请及时提出。

原文作者:GuoLei Song

原文链接:http://www.glmapper.com/2018/11/10/java-design-model-obs/

发表日期:November 10th 2018, 2:22:46 pm

更新日期:November 10th 2018, 5:23:02 pm

版权声明:转载请注明出处

CATALOG