公用技术——设计模式21——行为型模式——观察者模式

1、概念

  观察者模式是将subject的动态通知给Observer。在程序上动态体现为内部状态的改变,即某个属性值发生改变。

  Subject与Observer应该抽象的去理解,而不是直接翻译它的英文。例如在直播平台中,Subject代表主播,而Observer代表用户。在微博中Subject代表微博用户,Observer也代表微博的用户,二者可以是同一类事物。

  观察者模式涉及到三种角色

  1. Subject,代表被订阅的,或被观察的事物。
  2. Platform:平台,建立Subject与Observer之间的关系,并管理它们。
  3. Observer:观察者,主动去订阅某些事物

  为了方便理解观察者模式,结合生活中的直播场景。当用户进入直播平台时,

  1. 用户浏览主播列表,发现自己的主播,点击关注,此时相当于用户与主播之间建立了订阅关系。
  2. 主播上线,或者展开什么活动,平台将主播的动态通知给用户,方式很多,站内信,短信,邮件等等。
  3. 用户不在喜欢该主播,取消关注,平台不在推送任何信息。此时用户与主播取消了订阅关系。

  抽象的理解三种角色,用户代表Observer,直播平台代表Platform,主播代表Subject。

  一层关系,订阅关系,点击关注,建立这种关系,点击取消关注,取消这种关系。

  在观察者模式中,我个人感觉Observer是主动方,是它在掌握着订阅关系的建立与取消,所以我感觉把addObserver,removeObserver的方法放入到Subject方法中不太合适。

2、UML图

公用技术——设计模式21——行为型模式——观察者模式

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、示例

  • 求职者订阅求职网站的职位信息
  • 用户在直播平台关注喜欢的主播
  • 消费者在电商平台关注商品价格的动态