目录
观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者 一些产品的设计思路,都有这种模式的影子.
现在我们常说的基于事件驱动的架构,其实也是观察者模式的一种最佳实践。当我们观察某一个对象时,对象传递出的每一个行为都被看成是一个事件,观察者通过处理每一个事件来完成自身的操作处理。
观察者模式(observer pattern)的原始定义是:定义对象之间的一对多依赖关系,这样当一个对象改变状态时,它的所有依赖项都会自动得到通知和更新。

- /**
- * 抽象观察者
- * @author spikeCong
- * @date 2022/10/11
- **/
- public interface Observer {
-
- //update方法: 为不同观察者的更新(响应)行为定义相同的接口,不同的观察者对该方法有不同的实现
- public void update();
- }
-
- /**
- * 具体观察者
- * @author spikeCong
- * @date 2022/10/11
- **/
- public class ConcreteObserverOne implements Observer {
-
- @Override
- public void update() {
- //获取消息通知,执行业务代码
- System.out.println("ConcreteObserverOne 得到通知!");
- }
- }
-
- /**
- * 具体观察者
- * @author spikeCong
- * @date 2022/10/11
- **/
- public class ConcreteObserverTwo implements Observer {
-
- @Override
- public void update() {
- //获取消息通知,执行业务代码
- System.out.println("ConcreteObserverTwo 得到通知!");
- }
- }
- /**
- * 抽象目标类
- * @author spikeCong
- * @date 2022/10/11
- **/
- public interface Subject {
-
- void attach(Observer observer);
- void detach(Observer observer);
- void notifyObservers();
- }
-
- /**
- * 具体目标类
- * @author spikeCong
- * @date 2022/10/11
- **/
- public class ConcreteSubject implements Subject {
-
- //定义集合,存储所有观察者对象
- private ArrayList
observers = new ArrayList<>(); -
-
- //注册方法,向观察者集合中增加一个观察者
- @Override
- public void attach(Observer observer) {
- observers.add(observer);
- }
-
- //注销方法,用于从观察者集合中删除一个观察者
- @Override
- public void detach(Observer observer) {
- observers.remove(observer);
- }
-
- //通知方法
- @Override
- public void notifyObservers() {
- //遍历观察者集合,调用每一个观察者的响应方法
- for (Observer obs : observers) {
- obs.update();
- }
- }
- }
- public class Client {
-
- public static void main(String[] args) {
- //创建目标类(被观察者)
- ConcreteSubject subject = new ConcreteSubject();
-
- //注册观察者类,可以注册多个
- subject.attach(new ConcreteObserverOne());
- subject.attach(new ConcreteObserverTwo());
-
- //具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- subject.notifyObservers();
- }
- }
1 ) 未使用设计模式
- /**
- * 模拟买房摇号服务
- * @author spikeCong
- * @date 2022/10/11
- **/
- public class DrawHouseService {
-
- //摇号抽签
- public String lots(String uId){
- if(uId.hashCode() % 2 == 0){
- return "恭喜ID为: " + uId + " 的用户,在本次摇号中中签! !";
- }else{
- return "很遗憾,ID为: " + uId + "的用户,您本次未中签! !";
- }
- }
- }
-
- public class LotteryResult {
-
- private String uId; // 用户id
- private String msg; // 摇号信息
- private Date dataTime; // 业务时间
-
-
- //get&set.....
- }
-
- /**
- * 开奖服务接口
- * @author spikeCong
- * @date 2022/10/11
- **/
- public interface LotteryService {
-
- //摇号相关业务
- public LotteryResult lottery(String uId);
- }
-
-
- /**
- * 开奖服务
- * @author spikeCong
- * @date 2022/10/11
- **/
- public class LotteryServiceImpl implements LotteryService {
-
- //注入摇号服务
- private DrawHouseService houseService = new DrawHouseService();
-
- @Override
- public LotteryResult lottery(String uId) {
- //摇号
- String result = houseService.lots(uId);
-
- //发短信
- System.out.println("发送短信通知用户ID为: " + uId + ",您的摇号结果如下: " + result);
-
- //发送MQ消息
- System.out.println("记录用户摇号结果(MQ), 用户ID:" + uId + ",摇号结果:" + result);
-
- return new LotteryResult(uId,result,new Date());
- }
- }
-
- @Test
- public void test1(){
- LotteryService ls = new LotteryServiceImpl();
- String result = ls.lottery("1234567887654322");
- System.out.println(result);
- }
1 ) 使用观察者模式进行优化

- /**
- * 事件监听接口
- * @author spikeCong
- * @date 2022/10/11
- **/
- public interface EventListener {
-
- void doEvent(LotteryResult result);
- }
-
- /**
- * 短信发送事件
- * @author spikeCong
- * @date 2022/10/11
- **/
- public class MessageEventListener implements EventListener {
-
- @Override
- public void doEvent(LotteryResult result) {
- System.out.println("发送短信通知用户ID为: " + result.getuId() +
- ",您的摇号结果如下: " + result.getMsg());
- }
- }
-
- /**
- * MQ消息发送事件
- * @author spikeCong
- * @date 2022/10/11
- **/
- public class MQEventListener implements EventListener {
-
- @Override
- public void doEvent(LotteryResult result) {
- System.out.println("记录用户摇号结果(MQ), 用户ID:" + result.getuId() +
- ",摇号结果:" + result.getMsg());
- }
- }
事件处理
- /**
- * 事件处理类
- * @author spikeCong
- * @date 2022/10/11
- **/
- public class EventManager {
-
- public enum EventType{
- MQ,Message
- }
-
- //监听器集合
- Map
, List> listeners = new HashMap<>(); -
- public EventManager(Enum
... operations) { - for (Enum
operation : operations) { - this.listeners.put(operation,new ArrayList<>());
- }
- }
-
- /**
- * 订阅
- * @param eventType 事件类型
- * @param listener 监听
- */
- public void subscribe(Enum
eventType, EventListener listener) { - List
users = listeners.get(eventType); - users.add(listener);
- }
-
- /**
- * 取消订阅
- * @param eventType 事件类型
- * @param listener 监听
- */
- public void unsubscribe(Enum
eventType,EventListener listener) { - List
users = listeners.get(eventType); - users.remove(listener);
- }
-
- /**
- * 通知
- * @param eventType 事件类型
- * @param result 结果
- */
- public void notify(Enum
eventType, LotteryResult result) { - List
users = listeners.get(eventType); - for (EventListener listener : users) {
- listener.doEvent(result);
- }
- }
摇号业务处理
- /**
- * 开奖服务接口
- * @author spikeCong
- * @date 2022/10/11
- **/
- public abstract class LotteryService{
-
- private EventManager eventManager;
-
- public LotteryService(){
- //设置事件类型
- eventManager = new EventManager(EventManager.EventType.MQ, EventManager.EventType.Message);
- //订阅
- eventManager.subscribe(EventManager.EventType.Message,new MessageEventListener());
- eventManager.subscribe(EventManager.EventType.MQ,new MQEventListener());
- }
-
- public LotteryResult lotteryAndMsg(String uId){
- LotteryResult result = lottery(uId);
- //发送通知
- eventManager.notify(EventManager.EventType.Message,result);
- eventManager.notify(EventManager.EventType.MQ,result);
-
- return result;
- }
-
- public abstract LotteryResult lottery(String uId);
- }
-
- /**
- * 开奖服务
- * @author spikeCong
- * @date 2022/10/11
- **/
- public class LotteryServiceImpl extends LotteryService {
-
- //注入摇号服务
- private DrawHouseService houseService = new DrawHouseService();
-
- @Override
- public LotteryResult lottery(String uId) {
- //摇号
- String result = houseService.lots(uId);
-
- return new LotteryResult(uId,result,new Date());
- }
- }
测试
- @Test
- public void test2(){
- LotteryService ls = new LotteryServiceImpl();
- LotteryResult result = ls.lotteryAndMsg("1234567887654322");
- System.out.println(result);
- }
1) 观察者模式的优点
2) 观察者模式的缺点
3 ) 观察者模式常见的使用场景
4 ) JDK 中对观察者模式的支持
JDK中提供了Observable类以及Observer接口,它们构成了JDK对观察者模式的支持.
java.util.Observer 接口: 该接口中声明了一个方法,它充当抽象观察者,其中声明了一个update方法.
void update(Observable o, Object arg);
java.util.Observable 类: 充当观察目标类(被观察类) , 在该类中定义了一个Vector集合来存储观察者对象.下面是它最重要的 3 个方法。
void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。