Java设计模式之《观察者模式》及应用场景
一、观察者模式
观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监听者,类似监听器的存在,一旦被观察/监听的目标发生的情况,就会被监听者发现,这么想来目标发生情况到观察者知道情况,其实是由目标将情况发送到观察者的。
在现实生活中,警察抓小偷是一个典型的观察者模式「这以一个惯犯在街道逛街然后被抓为例子」,这里小偷就是被观察者,各个干警就是观察者,干警时时观察着小偷,当小偷正在偷东西「就给干警发送出一条信号,实际上小偷不可能告诉干警我有偷东西」,干警收到信号,出击抓小偷。这就是一个观察者模式
观察者模式多用于实现订阅功能的场景,例如微博的订阅,当我们订阅了某个人的微博账号,当这个人发布了新的消息,就会通知我们。
1.1 特点:
(a) subject 和 observer之间是松耦合的,各自独立实现,
(b) subject在发送广播通知的时候,不需要指定具体的observer,observer可以自行决定是否要订阅
(c) 遵循常用设计原则:高内聚,低耦合
1.2 观察者模式的效果有以下的优点:
第一、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。
由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。
第二、观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知,
观察者模式有下面的缺点:
第一、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
第二、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。
第三、如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
第四、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
1.3 观察者模式的应用场景:
1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
1.4 如何实现:
现在我们举一个类似的情况,并使用代码来实现,为大家提供一个比较明显的认识。
警察在找到嫌犯的时候,为了找到幕后主使,一般都会蹲点监察,这里我有三名便衣警察来蹲点监察2名嫌犯,三名便衣分别是:张昊天、石破天、赵日天,两名嫌犯是:大熊与黑狗,详见代码:
观察者接口:Observer
public interface Observer { void update(String message); }
定义便衣观察者:police
public class Police implements Observer{ @Override public void update(String message) { System.out.println("有人偷东西了"); } }
目标接口:BadGuy
public interface BadGuy{ void notice(String message); }
定义坏人类:(注意坏人类里面 有 police的类)
public class Bad1 implements BadGuy{ private Police police=new Police(); @Override public void notice(String message) { police.update(message); } }
测试类:MainTest
public class MainTest { public static void main(String[] args) { BadGuy badGuy=new Bad1(); Observer observer=new Police(); String message="又卖了一批货"; badGuy.notice(message); } }
测试结果:
有人偷东西了
通过上面的实例可以很明显的看出,观察者模式的大概模型,关键是什么呢?
关键点:
1、针对观察者与被观察者分别定义接口,有利于分别进行扩展。
2、重点就在被观察者的实现中:
(1)定义观察者集合,并定义针对集合的添加、删除操作,用于增加、删除订阅者(观察者)
(2)定义通知方法,用于将新情况通知给观察者用户(订阅者用户)
3、观察者中需要有个接收被观察者通知的方法。
如此而已!
观察者模式定义的是一对多的依赖关系,一个被观察者可以拥有多个观察者,并且通过接口对观察者与被观察者进行逻辑解耦,降低二者的直接耦合。
如此这般,想了一番之后,突然发现这种模式与桥接模式有点类似的感觉。
桥接模式也是拥有双方,同样是使用接口(抽象类)的方式进行解耦,使双方能够无限扩展而互不影响,其实二者还是有者明显的区别:
1、主要就是使用场景不同,桥接模式主要用于实现抽象与实现的解耦,主要目的也正是如此,为了双方的自由扩展而进行解耦,这是一种多对多的场景。观察者模式侧重于另一方面的解耦,侧重于监听方面,侧重于一对多的情况,侧重于一方发生情况,多方能获得这个情况的场景。
2、另一方面就是编码方面的不同,在观察者模式中存在许多独有的内容,如观察者集合的操作,通知的发送与接收,而在桥接模式中只是简单的接口引用。
我感觉不太直观,自己写了个例子:
package com.sankuai.qcs.regulation.observerModel; public interface actionBad { void addActionBad(actionPolice observer); void notice(String message); }
package com.sankuai.qcs.regulation.observerModel; /** * @description: com.sankuai.qcs.regulation.observerModel * @author: yinfuqing@meituan.com * @date: 2019-04-23-17-51 **/ public interface actionPolice { void update(String message,String name); }
package com.sankuai.qcs.regulation.observerModel; import java.util.ArrayList; import java.util.List; /** * @description: com.sankuai.qcs.regulation.observerModel * @author: yinfuqing@meituan.com * @date: 2019-04-23-17-56 **/ public class Bad implements actionBad { private String name="大熊"; private List<actionPolice> actionList=new ArrayList<>(); @Override public void addActionBad(actionPolice observer) { if(!actionList.contains(observer)){ actionList.add(observer); } } @Override public void notice(String message) { for(actionPolice actionPolice:actionList){ actionPolice.update(message,name); } } }
package com.sankuai.qcs.regulation.observerModel; /** * @description: com.sankuai.qcs.regulation.observerModel * @author: yinfuqing@meituan.com * @date: 2019-04-23-17-59 **/ public class mainTest { public static void main(String[] args) { actionBad badGuy1=new Bad(); actionPolice o1=new Police(); badGuy1.addActionBad(o1); String message="又进了一批货物"; badGuy1.notice(message); } }
package com.sankuai.qcs.regulation.observerModel; /** * @description: com.sankuai.qcs.regulation.observerModel * @author: yinfuqing@meituan.com * @date: 2019-04-23-17-52 **/ public class Police implements actionPolice { private String name1="警察1"; @Override public void update(String message, String name) { System.out.println("警察 "+name1+" : "+ name+" 那里有新情况 "+message); } }
本文来自博客园,作者:aspirant,转载请注明原文链接:https://www.cnblogs.com/aspirant/p/9442024.html