1、概念
观察者模式是将subject的动态通知给Observer。在程序上动态体现为内部状态的改变,即某个属性值发生改变。
Subject与Observer应该抽象的去理解,而不是直接翻译它的英文。例如在直播平台中,Subject代表主播,而Observer代表用户。在微博中Subject代表微博用户,Observer也代表微博的用户,二者可以是同一类事物。
观察者模式涉及到三种角色
- Subject,代表被订阅的,或被观察的事物。
- Platform:平台,建立Subject与Observer之间的关系,并管理它们。
- Observer:观察者,主动去订阅某些事物
为了方便理解观察者模式,结合生活中的直播场景。当用户进入直播平台时,
- 用户浏览主播列表,发现自己的主播,点击关注,此时相当于用户与主播之间建立了订阅关系。
- 主播上线,或者展开什么活动,平台将主播的动态通知给用户,方式很多,站内信,短信,邮件等等。
- 用户不在喜欢该主播,取消关注,平台不在推送任何信息。此时用户与主播取消了订阅关系。
抽象的理解三种角色,用户代表Observer,直播平台代表Platform,主播代表Subject。
一层关系,订阅关系,点击关注,建立这种关系,点击取消关注,取消这种关系。
在观察者模式中,我个人感觉Observer是主动方,是它在掌握着订阅关系的建立与取消,所以我感觉把addObserver,removeObserver的方法放入到Subject方法中不太合适。
2、UML图

3、代码
User类,在直播场景中扮演着用户角色,在观察者模式中扮演着Observer角色,它是订阅关系的主动方。
/**
*
* @Title: User.java
* @Package observer
* @Description: 扮演着Observer的角色。
* @version V1.0
*/
public class User {
// 平台对象
private Platform platform;
public User(Platform platform) {
this.platform = platform;
}
// 在实际应用中表现为关联表中多添加一条记录
public void interestIn(Streamer streamer) {
platform.bindRelationShip(this,streamer);
}
// 在实际应用中表现为关联表中少一条记录
public void canceliInterestIn(Streamer streamer) {
platform.unbindRelationShip(this,streamer);
}
}
Streamer类,在直播模型中扮演主播角色,在观察者模式中扮演Subject角色。
public class Streamer {
// 平台对象
private Platform platform;
// 主播的唯一标识,房间号,ID,随意一个标识
private String id;
public Streamer(Platform platform) {
this.platform = platform;
}
/**
*
* @Title: open
* @Description: 主播开播了
*/
public void open() {
List<User> users = platform.selectUsersByStreamer(id);
for (User currentUser : users) {
// 以邮箱方式通知用户开播了
platform.notifyUser(currentUser, this.id + "已经开播了", NotifyMethod.EMAIL);
}
}
/**
*
* @Title: close
* @Description: 主播关闭直播了
*/
public void close() {
System.out.println("主播关闭了直播间");
}
/**
*
* @Title: makeActivity
* @Description: 主播发起了某个活动
*/
public void makeActivity() {
List<User> users = platform.selectUsersByStreamer(id);
for (User currentUser : users) {
// 以站内信的方式通知用户开播了
platform.notifyUser(currentUser, this.id + "正在发起某某活动,快来参与呀", NotifyMethod.INNER_MESSAGE);
}
}
}
Platform,在直播模型中扮演着平台的角色,在观察者模式中扮演着”平台”的角色,主要是管理和为订阅关系提供平台。当平台不复存在时,这种订阅关系随之消失。它并不是一个具体的类或者功能,这里只是需要这样一个角色,把所有的功能抽象到这个角色中。
public class Platform {
/**
*
* @Title: selectUsersByStreamer
* @Description: 根据主播,查询所有关注他的用户
* @param id
* @return
*/
public List<User> selectUsersByStreamer(String id) {
// 有可能在数据库表中的关联表中执行一次查询
return null;
}
/**
*
* @Title: notifyUser
* @Description: 通知用户
* @param user
* @param string
* @param method
*/
public void notifyUser(User user, String string, NotifyMethod method) {
switch (method) {
case EMAIL: {
// 发送邮件
break;
}
case INNER_MESSAGE: {
// 发送站内信
break;
}
case SHORT_MESSAGE_SERVICE: {
// 发送短信
break;
}
}
}
/**
*
* @Title: bindRelationShip
* @Description: 用户关注主播
* @param user
* @param streamer
*/
public void bindRelationShip(User user, Streamer streamer) {
// 有可能在数据库的关联表中添加一条记录
}
/**
*
* @Title: unbindRelationShip
* @Description: 用户取消关注主播
* @param user
* @param streamer
*/
public void unbindRelationShip(User user, Streamer streamer) {
// 有可能在数据库的关联表中删除一条记录
}
}
NotifyMethod,通知方式,是个枚举类
public enum NotifyMethod {
/**
* 站内信
*/
INNER_MESSAGE,
/**
* 邮箱
*/
EMAIL,
/**
* 短信
*/
SHORT_MESSAGE_SERVICE
}
4、讨论
在观察者模式中,涉及到三种角色,Subject,Platform,Observer。
涉及到两种关系,一种是Subject与Observer之间的1对多的关联关系。另外一种是Subject与Observer之间的订阅关系。很明显如果没有平台,订阅关系也失去了意义。
涉及到三个问题,第一个问题是将哪些”动态”推送给Observer,第二个问题是推送的方式,短信,站内信,邮件等等,第三个问题是推送的频率,即when,何时推送,间隔周期,重复次数等等。
5、示例
- 求职者订阅求职网站的职位信息
- 用户在直播平台关注喜欢的主播
- 消费者在电商平台关注商品价格的动态