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、示例
- 求职者订阅求职网站的职位信息
- 用户在直播平台关注喜欢的主播
- 消费者在电商平台关注商品价格的动态