3分钟看懂设计模式02:观察者模式

打印 上一主题 下一主题

主题 908|帖子 908|积分 2724

一、什么是观察者模式

观察者模式又叫做发布-订阅模式或者源-监视器模式
结合它的各种别名大概就可以明白这种模式是做什么的。
其实就是观察与被观察,一个对象(被观察者)的状态改变会被通知到观察者,并根据通知产生各自的不同的行为。
以下为《设计模式的艺术》中给出的定义:
观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新
二、观察者模式的4个角色

Subject(目标)

Subject是被观察的对象。
Subject可以是接口、抽象类或者具体类。
它一般有4个要素
• 一个观察者集合,一般用Vector。
• 增加观察者的方法。
• 删除观察者的方法。
• 通知方法notify()。
ConcreteSubject(具体目标)

是Subject的子类,没什么特殊的,如果有抽象方法需要实现就实现,没有的话这个类不写也行。
Observer(观察者)

一般是个接口,声明一个update()方法。
ConcreteObserver(具体观察者)

刚开始学的话先会最简单的形式就可以了,就直接实现Observer接口,实现update()方法就完了。
三、观察者模式2个代码实现案例

通过2个案例,基本就可以知道观察者模式是怎么回事了,对照观察者模式的4个角色,敲一遍。
单纯熟悉4个角色的案例

先写一个被观察者Subject
  1. public abstract class Subject {
  2.     // 存放观察者的集合
  3.     private Vector<Observer> obs = new Vector<>();
  4.     public void addObserver(Observer obs) {
  5.         this.obs.add(obs);
  6.     }
  7.     public void delObserver(Observer obs) {
  8.         this.obs.remove(obs);
  9.     }
  10.     // 通知方法,写成抽象方法让ConcreteSubject实现也一样的
  11.     protected void notifyObserver() {
  12.         for (Observer ob : obs) {
  13.             ob.update();
  14.         }
  15.     }
  16.     // 这里也是可写可不写,根据业务需求
  17.     public abstract void doSomething();
  18. }
复制代码
再写一个ConcreteSubject
  1. public class ConcreteSubject extends Subject {
  2.     @Override
  3.     public void doSomething() {
  4.         System.out.println("被观察者事件发生改变");
  5.         this.notifyObserver();
  6.     }
  7. }
复制代码
观察者接口Observer
  1. public interface Observer {
  2.     public void update();
  3. }
复制代码
观察者实现类ConcreteObserver,我们这里给出2个观察者
  1. public class ConcreteObserver01 implements Observer {
  2.     @Override
  3.     public void update() {
  4.         System.out.println("观察者01收到状态变化信息,并进行处理...");
  5.     }
  6. }
复制代码
  1. public class ConcreteObserver02 implements Observer {
  2.     @Override
  3.     public void update() {
  4.         System.out.println("观察者02收到状态变化信息,并进行处理...");
  5.     }
  6. }
复制代码
最后给出一个测试类,测试运行一个看看效果,看不明白代码就debug一下捋一捋。
以下代码的意思就是:
你得有东西被观察被监视吧?
所以先创建一个被观察者,比如我的账户余额。
你得设置哪些对象在观察、监视我的账户吧?
那就添加2个,我和我老婆。
然后就坐等账户余额有变化。
一旦发工资,状态变化!!!
dosomething()!
notifyObserver()!
遍历观察者列表!
update()!
这个时候我和我老婆分别对应各自实现的update()方法。
我马上去买了游戏。
我老婆马上去买了化妆品。
(不过这个例子好像不太合适,因为update方法里又会导致账户余额的变化,循环起来了,不过大概明白咋回事就行了。)
  1.     public class Client {
  2.         public static void main(String[] args) {
  3.             ConcreteSubject subject = new ConcreteSubject();
  4.             subject.addObserver(new ConcreteObserver01());
  5.             subject.addObserver(new ConcreteObserver02());
  6.             subject.doSomething();
  7.         }
  8.     }
复制代码
盟友受攻击发通知,其他盟友做出响应

这个例子也是《设计模式的艺术》给出的案例。
我们的需求如下:
联盟成员收到攻击→发送通知给盟友→盟友做出响应。
如果按照上述思路设计,则每个成员必须持有其他所有成员的状态信息,导致系统开销过大。所以我们引入一个战队控制中心来统一维护所有战队成员信息。

依然是我们四步走:
先创建一个Subject被观察者,这里是AllyControlCenter控制中心
  1.     public abstract class AllyControlCenter {
  2.         /**
  3.          * 战队名称
  4.          */
  5.         protected String allyName;
  6.         /**
  7.          * 定义一个集合,用来存储具体观察者,也就是战队成员
  8.          */
  9.         protected Vector<Observer> players = new Vector<Observer>();
  10.         /**
  11.          * 加入战队
  12.          */
  13.         public void join(Observer observer) {
  14.             System.out.println(observer.getName() + "加入" + this.allyName + "战队!");
  15.             players.add(observer);
  16.         }
  17.         /**
  18.          * 退出战队
  19.          */
  20.         public void quit(Observer observer) {
  21.             System.out.println(observer.getName() + "退出" + this.allyName + "战队!");
  22.             players.remove(observer);
  23.         }
  24.         /**
  25.          * 声明抽象通知方法
  26.          * @param name
  27.          */
  28.         public abstract void notifyObserver(String name);
  29.         /**
  30.          * 设置成员变量方法
  31.          * @param allyName
  32.          */
  33.         public void setAllyName(String allyName) {
  34.             this.allyName = allyName;
  35.         }
  36.         /**
  37.          * 获取成员变量方法
  38.          * @return
  39.          */
  40.         public String getAllyName() {
  41.             return this.allyName;
  42.         }
  43.     }
复制代码
再创建一个ConcreteSubject具体被观察者ConcreteAllyControlCenter
  1.     public class ConcreteAllayControlCenter extends AllyControlCenter {
  2.         public ConcreteAllayControlCenter(String name) {
  3.             System.out.println(name + "战队组建成功!");
  4.             System.out.println("-------------");
  5.             this.allyName = name;
  6.         }
  7.         /**
  8.          * 实现通知方法
  9.          * @param name
  10.          */
  11.         @Override
  12.         public void notifyObserver(String name) {
  13.             System.out.println(this.allyName + "战队紧急通知,盟友" + name + "遭受敌人攻击");
  14.             // 遍历观察者集合,调用每一个盟友(除了自己)的支援方法
  15.             for (Observer obs : players) {
  16.                 if (!obs.getName().equalsIgnoreCase(name)) {
  17.                     obs.help();
  18.                 }
  19.             }
  20.         }
  21.     }
复制代码
创建一个抽象观察者Observer
  1.     public interface Observer {
  2.         public String getName();
  3.         public void setName(String name);
  4.         /**
  5.          * 声明支援盟友的方法
  6.          */
  7.         public void help();
  8.         /**
  9.          * 声明遭受攻击的方法
  10.          */
  11.         public void beAttacked(AllyControlCenter acc);
  12.     }
复制代码
在创建具体观察者Player
  1. public class Player implements Observer {
  2.     private String name;
  3.     public Player(String name) {
  4.         this.name = name;
  5.     }
  6.     @Override
  7.     public String getName() {
  8.         return this.name;
  9.     }
  10.     @Override
  11.     public void setName(String name) {
  12.         this.name = name;
  13.     }
  14.     /**
  15.      * 支援盟友的方法实现
  16.      */
  17.     @Override
  18.     public void help() {
  19.         System.out.println("坚持住,"+this.name +"来救你了!");
  20.     }
  21.     /**
  22.      * 遭受攻击的方法实现
  23.      * 当遭受攻击时,调用战队控制中心类的通知方法notifyObserver()来通知盟友
  24.      * @param acc
  25.      */
  26.     @Override
  27.     public void beAttacked(AllyControlCenter acc) {
  28.         System.out.println(this.name + "被攻击!");
  29.         acc.notifyObserver(name);
  30.     }
  31. }
复制代码
测试运行
  1. public class Client {
  2.     public static void main(String[] args) {
  3.         // 定义被观察者
  4.         AllyControlCenter acc = new ConcreteAllayControlCenter("金庸群侠");
  5.         // 定义4个观察者
  6.         Observer player1 = new Player("杨过");
  7.         Observer player2 = new Player("令狐冲");
  8.         Observer player3 = new Player("张无忌");
  9.         Observer player4 = new Player("段誉");
  10.         acc.join(player1);
  11.         acc.join(player2);
  12.         acc.join(player3);
  13.         acc.join(player4);
  14.         // 某成员遭受攻击
  15.         player1.beAttacked(acc);
  16.     }
  17. }
复制代码
四、我什么时候用观察者模式?


  • 事件处理系统:例如,用户界面框架中,当用户进行某些操作(如点击按钮、移动鼠标等)时,可以使用观察者模式来通知相关的处理程序。
  • 数据订阅与发布系统:在需要向多个客户端发布数据更新的场景中,例如股票行情显示、新闻更新等,可以使用观察者模式。
  • 跨系统的消息交换:例如,在微服务架构中,服务间的事件可以通过观察者模式进行通信,确保各服务间的解耦。
  • 状态监控和警报系统:在需要监控某些状态并在特定条件下发送警报的系统中,观察者模式可以用来实现监控对象和警报系统之间的通信。
  • 配置管理:当系统配置信息发生变更时,使用观察者模式可以实时通知各个使用配置的组件进行相应的调整。
往期推荐:
● 师爷,翻译翻译什么叫AOP
翻译,师爷师爷什么叫事务
纪念JDBC
● SpringBoot实现动态数据源配置
● 聚簇索引、回表与覆盖索引
Java锁到底是个什么东西


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

玛卡巴卡的卡巴卡玛

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表