B站视频链接:审核中。。。。。。。。
观察者模式的定义
在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。
场景
--聊天室程序的创建。服务器创建好后, A,B,C三个客户端连.上来公开聊天。A向服务器发送数据,服务器端聊天数据改变。我们希望将这些聊天数据分别发给其他在线的客户。也就是说,每个客户端需要更新服务器端得数据。
--网站上,很多人订阅了”java主题”的新闻。当有这个主题新闻时,就会将这些新闻发给所有订阅的人。
--大家一起玩CS游戏时,服务器需要将每个人的方位变化发给所有的客户。
上面这些场景,我们都可以使用观察者模式来处理。我们可以把多个订阅者、客户称之为观察者;需要同步给多个订阅者的数据封装到对象中,称之为目标。
核心:消息发布
--观察者模式主要用于1 : N的通知。当一个对象(目标对象Subject或Objservable)的状态变化时,他需要及时告知一系列对象(观察者对象,Observer) ,令他们做出响应。
--通知观察者的方式:消息订阅
●推
--每次都会把通知以广播方式发送给所有观察者,所有观察者只能被动接收。
●拉
--观察者只要知道有情况即可。至于什么时候获取内容,获取什么内容,都可以自主决定。
四个角色
抽象主题角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
具体主题角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。
具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。
UML类图
举例:实现消息推送给订阅了的观察者,没有订阅的观察者无法收到消息推送。
抽象主题类和观察者抽象接口
package Observer; /** * 定义抽象主题 * @author 唐磊 * */ public abstract class Subject { abstract void registerObserver(Observer obs);//添加Observer方法 abstract void removeObserver(Observer obs) ;//移除Observer方法 //通知所有观察者更新状态 abstract void notifyAllObservers() ; } package Observer; /** * 定义观察者抽象接口 * @author 唐磊 * */ public interface Observer { void update(Subject subject); }
具体主题和具体的观察者
package Observer; import java.util.ArrayList; /** * 定义具体主题类 * @author 唐磊 * */ public class ConcreteSubject extends Subject{ private String message; //存放观察者的容器 ArrayList<Observer> list = new ArrayList<Observer>(); public String getMessage() { return message; } //目标对象的状态发生了变化,通知所有的观察者 public void setMessage(String message) { this.message = message; System.out.println("QQ推送了更新消息:"+message); this.notifyAllObservers(); } @Override void registerObserver(Observer obs) { list.add(obs); } @Override void removeObserver(Observer obs) { list.remove(obs); } @Override void notifyAllObservers() { for (Observer obs : list) { obs.update(this); } } } package Observer; /** * 定义观察者具体类 * @author 唐磊 * */ public class ConcreteObserver implements Observer{ private String name; private String myMessage;//观察者订阅的目标对象的message public ConcreteObserver(String name){ this.name = name; } @Override public void update(Subject subject) { myMessage = ((ConcreteSubject)subject).getMessage(); read(); } public void read() { System.out.println(name+"收到了推送消息:"+myMessage); } public String getMyMessage() { return myMessage; } public void setMyMessage(String myMessage) { this.myMessage = myMessage; } }
Client
package Observer; public class Client { public static void main(String[] args) { //目标对象 ConcreteSubject subject = new ConcreteSubject(); //创建多个观察者 ConcreteObserver obs1 = new ConcreteObserver("Lisa"); ConcreteObserver obs2 = new ConcreteObserver("James"); ConcreteObserver obs3 = new ConcreteObserver("Mary"); //将这些观察者加入Subject的Observer容器中 subject.registerObserver(obs1); subject.registerObserver(obs2); subject.registerObserver(obs3); //改变subject目标对象的状态,设置推送消息 subject.setMessage("新冠疫情发布!"); System.out.println("----------------------------------"); subject.removeObserver(obs1);//Lisa取消了订阅 System.out.println("Lisa取消了订阅。"); subject.setMessage("美国新冠确诊人数突破100万人!!!"); } }
运行结果
可以看到lisa取消订阅后,无法收到消息推送。