1:观察者模式
观察者模式是一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时通知多个“观察”该对象的其他对象。
2:观察者模式问题
在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。
拿双十一举例:如果某一商品决定在双十一当天零点进行大促销,如果他选择不通知任何人,那么他这一件商品的销量就只有听天由命了,一件都卖不去也说停;如果他选择通知所有浏览过这件商品的人,也许在这些人里有想买这件商品的人会选择购买,但是也会有很多不需要这件商品的人收到骚扰。
在生活中,他们是怎么解决这件事的呢?
3:观察者模式解决方案
在生活中,这些购物软件给店铺提供了一个渠道:对于那些关注过这家店铺,或者在这家店铺购买过的商品的潜在用户。店家可以通过软件提供的消息推送方式,或者购买记录的个人信息(虽然各种短信也挺骚扰的)。告诉你:我要降价了,快来买哦。这样店家也达成通知用户提高销量的目的,买家也可以低价买入需要的物品。
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,
4:观察者模式结构
- 发布者(Publisher)会向其他对象发送值得关注的事件。事件会在发布者自身状态改变或执行特定行为后发生。发布者中包含一个允许新订阅者加入和当前订阅者离开列表的订阅构架。
- 当新事件发生时,发送者会遍历订阅列表并调用每个订阅者对象的通知方法。该方法是在订阅者接口中声明的。
- 订阅者(Subscriber)接口声明了通知接口。在绝大多数情况下,该接口仅包含一个 update更新方法。该方法可以拥有多个参数,使发布者能在更新时传递事件的详细信息。
- 具体订阅者(Concrete Subscribers)可以执行一些操作来回应发布者的通知。所有具体订阅者类都实现了同样的接口,因此发布者不需要与具体类相耦合。
- 订阅者通常需要一些上下文信息来正确地处理更新。因此,发布者通常会将一些上下文数据作为通知方法的参数进行传递。发布者也可将自身作为参数进行传递, 使订阅者直接获取所需的数据。
- 客户端(Client)会分别创建发布者和订阅者对象,然后为订阅者注册发布者更新。
为什么还要抽象出一个统一的订阅者呢?如果你现在有多个类型的发布者,而又想使订阅者可以兼容所有发布者,不需要各种渠道订阅不同的发布者。例如你订购了每天的新闻通知,如果你想微信通知需要微信订阅,选软件通知需要单独下一个软件,选邮箱又需要单独在邮箱发出申请。我相信你极其厌恶这份报纸。而现在有类似的一个新闻软件,只需要在软件上选择是通过何种方式,他就会按照设定的方案通知你。选什么已经不用再说了吧。
代码示例:
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
| import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap;
class EventManager {
private static ConcurrentMap<String, List<EventListener>> listeners = new ConcurrentHashMap<>();
public EventManager(String... operations) { for (String operation : operations) { listeners.put(operation, new ArrayList<>()); } }
public void addListener(EventListener listener, String eventType) { List<EventListener> eventListeners = listeners.get(eventType); eventListeners.add(listener); }
public void subListener(EventListener listener, String eventType) { List<EventListener> eventListeners = listeners.get(eventType); eventListeners.remove(listener); }
public void notify(String eventType, String data) { List<EventListener> eventListeners = listeners.get(eventType); eventListeners.forEach(e -> e.update(eventType, data)); } }
class Publisher {
public EventManager manager; private String data;
public Publisher() { this.manager = new EventManager("1", "2"); }
public void publishOne() { this.data = "发布一号信息"; manager.notify("1", data); }
public void publishTwo() { this.data = "发布二号信息"; manager.notify("2", data); } }
interface EventListener {
void update(String eventType, String data); }
class EmailNotifyListener implements EventListener { private String email;
public EmailNotifyListener(String email) { this.email = email; }
@Override public void update(String eventType, String data) { System.out.println("邮箱发送到 " + email + " 事件类型:" + eventType + " 信息:" + data); } }
class WeChatNotifyListener implements EventListener { private String wechat;
public WeChatNotifyListener(String wechat) { this.wechat = wechat; }
@Override public void update(String eventType, String data) { System.out.println("微信发送到 " + wechat + " 事件类型:" + eventType + " 信息:" + data);
} }
public class ListenerDemo {
public static void main(String[] args) { Publisher publisher = new Publisher(); publisher.manager.addListener(new EmailNotifyListener("123@qq.com"), "1"); publisher.manager.addListener(new WeChatNotifyListener("123"), "2"); publisher.manager.addListener(new EmailNotifyListener("456@qq.com"), "1"); publisher.manager.addListener(new WeChatNotifyListener("456"), "1");
publisher.publishOne(); publisher.publishTwo();
}
}
|
输出如下:
1 2 3 4
| 邮箱发送到 123@qq.com 事件类型:1 信息:发布一号信息 邮箱发送到 456@qq.com 事件类型:1 信息:发布一号信息 微信发送到 456 事件类型:1 信息:发布一号信息 微信发送到 123 事件类型:2 信息:发布二号信息
|
5:观察者模式适用场景
- 当一个对象状态的改变需要改变其他对象,或实际对象是事先未知的或动态变化的时,可使用观察者模式。
- 当应用中的一些对象必须观察其他对象时,可使用该模式。但仅能在有限时间内或特定情况下使用。
6:观察者模式优缺点
优点 |
缺点 |
开闭原则。你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)。 |
订阅者的通知顺序是随机的。 |
你可以在运行时建立对象之间的联系。 |
|
7:观察者模式与其他模式关系
- 责任链模式、命令模式、中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:
- 责任链按照顺序将请求动态传递给一系列的潜在接收者,直至其中一名接收者对请求进行处理。
- 命令在发送者和请求者之间建立单向连接。
- 中介者清除了发送者和请求者之间的直接连接,强制它们通过一个中介对象进行间接沟通。
- 观察者允许接收者动态地订阅或取消接收请求。
- 中介者和观察者之间的区别往往很难记住。在大部分情况下,你可以使用其中一种模式,而有时可以同时使用。让我们来看看如何做到这一点。
中介者的主要目标是消除一系列系统组件之间的相互依赖。这些组件将依赖于同一个中介者对象。观察者的目标是在对象之间建立动态的单向连接,使得部分对象可作为其他对象的附属发挥作用。
有一种流行的中介者模式实现方式依赖于观察者。中介者对象担当发布者的角色,其他组件则作为订阅者,可以订阅中介者的事件或取消订阅。当中介者以这种方式实现时,它可能看上去与观察者非常相似。
当你感到疑惑时,记住可以采用其他方式来实现中介者。例如,你可永久性地将所有组件链接到同一个中介者对象。这种实现方式和观察者并不相同,但这仍是一种中介者模式。
假设有一个程序,其所有的组件都变成了发布者,它们之间可以相互建立动态连接。这样程序中就没有中心化的中介者对象,而只有一些分布式的观察者。
8:观察者模式举例
观察者模式又叫发布订阅模式,公众号可以形象的说明这个模式,公众号是发布者,我们是订阅者,微信是客户端。点击订阅之后可以接受公众号推送的文章
9:参考