观察者模式和发布订阅模式
一、概念
观察者(Observer),又称发布-订阅(Publish-Subscrice),属于23中设计模式之一。 发布订阅模式定义了一种一对多的依赖关系,让多个订阅者对象监听某一个主题对象。这个主题对象在自身状态变化时,会通知所有订阅者对象,使它们能够自动更新自己的状态。
二、应用场景以租房为例,小明到中介租房,合适的房源已经没有了,所以小明就会留下自己的联系方式给中介,一旦有合适的房源,中介就会通知小明。而中介对接向小明一样需求的客户有很多个,中介会通知他对接的所有顾客,这就是一个发布订阅的案例。其中小明就是订阅者,中介就是发布者,小明在中介里进行订阅之后,中介在找到符合房源后会进行发布,让订阅者收到消息。
类似这种就是发布订阅的案例,生活中的公众号、博客关注博主等等都是发布订阅的应用。
观察者模式 定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
观察者模式的别名包括发布-订阅模式、模型-视图等。
细究的话,发布订阅和观察者有些不同,可以理解成发布订阅模式属于广义上的观察者模式。
发布-订阅设计模式
在发布-订阅模式,消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者,叫做订阅者。
意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做订阅中心,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。换句话说,发布-订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。
所以观察者模式和发布订阅者模式其实是大同小异,原理都是一样的,区别就在于有没有订阅中心。在GOF23中设计模式中是只有观察者模式,发布订阅模式是不存在的。有些人认为观察者和发布订阅是同一种模式,而有些人认为这是两种设计模式,关键看你怎么理解。
订阅者实体类Subscriber
public class Subscriber { private String name; public Subscriber(String name) { this.name = name; } public String getName() { return name; } }
发布者实体类Publisher
public class Publisher { private String name; //发布者的名字 public Publisher(String name) { this.name = name; } public String getName() { return name; } }
发布订阅中心SubPubCentral,发布订阅中心需要实现该接口
public interface SubPubCentral { public boolean subscribe(Publisher publisher, Subscriber subscriber); public boolean unsubscribe(Publisher publisher, Subscriber subscriber); public void publish(Publisher publisher, String message); }
发布订阅中心实现类SubPubCentralImpl
public class SubPubCentralImpl implements SubPubCentral { private static Map> PubSubMap = ne HashMap<>(); //存放所有的发布者的对应订阅者 @Override public boolean subscribe(Publisher publisher, Subscriber subscriber) { try { Set subscriberSet = PubSubMap.get(publisher.getName()); //拿到当前发布者的所有订阅者 if (subscriberSet == null) //为空,之前不存在订阅者 subscriberSet = ne HashSet<>(); boolean added = subscriberSet.add(subscriber.getName()); //添加订阅者 if (added) //添加订阅者成功。 return PubSubMap.put(publisher.getName(), subscriberSet) != null; return false; //订阅者添加失败或者该订阅之前则订阅了发布者 } catch (Exception e) { e.printStackTrace(); } return false; } @Override public boolean unsubscribe(Publisher publisher, Subscriber subscriber) { try { Set subscriberSet = PubSubMap.get(publisher.getName()); if (subscriberSet == null) return false; boolean removed = subscriberSet.remove(subscriber.getName()); //删除取消订阅者 if (removed) PubSubMap.put(publisher.getName(), subscriberSet); //更新订阅者列表 return removed; } catch (Exception e) { e.printStackTrace(); } return false; } @Override public void publish(Publisher publisher, String message) { Set subscriberSet = PubSubMap.get(publisher.getName());//拿到当前发布者的所有订阅者 //遍历订阅者发送消息。 此处简单实现 只需要打印出拿到的所有订阅者即可 for (String subscriber : subscriberSet) { System.out.println(subscriber + "接收到" + publisher.getName() + "发布的新消息:" + message); } } }
控制层,负责控制发布者的一系列行为
public class PublisherController { private SubPubCentral subPubCentral; //订阅发布中心。 public PublisherController(SubPubCentral subPubCentral) { this.subPubCentral = subPubCentral; } public void publish(Publisher publisher, String message) { subPubCentral.publish(publisher, message); } }
控制层,负责控制订阅者的一系列行为
public class SubscriberController { private SubPubCentral subPubCentral; public SubscriberController(SubPubCentral subPubCentral) { this.subPubCentral = subPubCentral; } public void subscribe(Publisher publisher, Subscriber subscriber) { subPubCentral.subscribe(publisher, subscriber); } public void unsubscribe(Publisher publisher, Subscriber subscriber) { subPubCentral.unsubscribe(publisher, subscriber); } }
模拟发布订阅
public class Test { public static void main(String[] args) { //创建一个发布订阅中心 SubPubCentral subPubCentral = ne SubPubCentralImpl(); //创建发布者和订阅者的控制层 PublisherController publisherController = ne PublisherController(subPubCentral); SubscriberController subscriberController = ne SubscriberController(subPubCentral); //创建订阅者对象 Subscriber subscriber1=ne Subscriber("小明"); Subscriber subscriber2=ne Subscriber("小李"); //创建发布者对象 Publisher publisher=ne Publisher("中介"); //订阅者订阅 subscriberController.subscribe(publisher, subscriber1); subscriberController.subscribe(publisher, subscriber2); //发布者发布新消息 publisherController.publish(publisher, "天河区有新房出租!"); System.out.println("n----------------------------------------------------n"); //订阅者取消订阅 subscriberController.unsubscribe(publisher, subscriber1); //发布者发布新消息 publisherController.publish(publisher, "番禺区有新房出租!"); } }
执行结果
订阅者可以调用subscribe方法订阅对象和调用unsubscribe方法取消订阅对象,而发布者可以调用publish方法发布新消息给所有订阅他的对象。而不管是建立/取消订阅关系还是发布新消息都是有发布订阅中心PublisherController统一调用实现,就像现实中租房的中介,不管是租客还是房主,都是通过中介进行调度、通知。
2、观察者实现代码(无订阅中心)主题Subject
public interface Subject { public void attach(ConcreteObserver concreteObserver); public void notify(String msg); public void detach(ConcreteObserver concreteObserver); }
观察者接口
public interface Observer { public void update(String subject, String msg); }
主题实现类ConcreteSubject
public class ConcreteSubject implements Subject { //定义一个集合来储存观察者 private ArrayListevents = ne ArrayList<>(); //订阅的主题名字 String name; public ConcreteSubject(String name) { this.name = name; } @Override public void attach(ConcreteObserver concreteObserver) { events.add(concreteObserver); } @Override public void notify(String msg) { for (int i = 0; i < events.size(); i++) { ConcreteObserver concreteObserver = events.get(i); concreteObserver.update(this.name, msg); } } @Override public void detach(ConcreteObserver concreteObserver) { events.remove(concreteObserver); } }
观察者实例
public class ConcreteObserver implements Observer { private String name; private Receiver receiver; public interface Receiver { void onMessage(String publisher, String msg); } //创建观察者 public ConcreteObserver(String name) { this.name = name; } //创建观察者也可以加个回调函数,用来将消息调出去 public ConcreteObserver(String name, Receiver receiver) { this.name = name; this.receiver = receiver; } @Override public void update(String subject, String msg) { System.out.println(this.name + "收到" + subject + "发送的消息:" + msg); if (receiver != null) { receiver.onMessage(subject, msg); } } }
模拟实现
public class Test { public static void main(String[] args) { //创建观察者 ConcreteObserver concreteObserver1 = ne ConcreteObserver("小明", ne ConcreteObserver.Receiver() { @Override public void onMessage(String publisher, String msg) { //收到消息 System.out.println(publisher + "说" + msg); } }); ConcreteObserver concreteObserver2 = ne ConcreteObserver("小华"); ConcreteObserver concreteObserver3 = ne ConcreteObserver("小红"); //创建主题 ConcreteSubject concreteSubject = ne ConcreteSubject("BOSS"); concreteSubject.attach(concreteObserver1); concreteSubject.attach(concreteObserver2); concreteSubject.attach(concreteObserver3); concreteSubject.notify("今天放假!"); } }
运行结果
空调维修
- 温岭冰箱全国统一服务热线-全国统一人工【7X2
- 荆州速热热水器维修(荆州热水器维修)
- 昆山热水器故障码5ER-昆山热水器故障码26
- 温岭洗衣机24小时服务电话—(7X24小时)登记报
- 统帅热水器售后维修服务电话—— (7X24小时)登
- 阳江中央空调统一电话热线-阳江空调官方售后电
- 乌鲁木齐阳春燃气灶厂家服务热线
- 珠海许昌集成灶售后服务电话-全国统一人工【
- 乌鲁木齐中央空调维修服务专线-乌鲁木齐中央空
- 新沂热水器故障电话码维修-新沂热水器常见故障
- 诸城壁挂炉24小时服务热线电话
- 靖江空调24小时服务电话-——售后维修中心电话
- 空调室外滴水管维修(空调室外排水管维修)
- 九江壁挂炉400全国服务电话-(7X24小时)登记报修
- 热水器故障码f.22怎么解决-热水器f0故障解决方法
- 营口热水器售后维修服务电话—— 全国统一人工