高手眼中的观察者模式有什么不一样
2020/6/6 13:25:33
本文主要是介绍高手眼中的观察者模式有什么不一样,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
观察者模式,是使用很多的一种模式,初次了解,只是根据入门demo写个例子,但是不知道用在哪,怎么用,很教科书。
个人很喜欢比较实战的的博客或者资料。
最近又恶补了一把,感觉有点如梦初醒,一切都串起来了。
基础部分
一、观察者模式的基本经典结构
二、入门小demo
经典小demo1:
Observer
/** * 观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象 */ public interface Observer { /** * 更新的接口 */ public void update(Subject subject); }
ConcreteObserver
//具体观察者对象,实现更新的方法,使用自身的状态和 public class ConcreteObserver implements Observer { @Override public void update(Subject subject) { //具体的实现 //这里可能需要更新观察者的状态,使其与目标的状态保持一致 String message = ((ConcreteSubject) subject).getSubjectState(); System.out.println("收到一通知: 获取到的状态是: " + message); } }
Subject
/** * 目标对象,它知道观察它的观察者,并提供注册和删除观察者的接口 */ public class Subject { /** * 用来保存注册的观察者对象 */ private List<Observer> observers = new ArrayList<>(); /** * 注册观察者对象 */ public void attach(Observer observer){ observers.add(observer); } /** * 删除观察者对象 */ public void detach(Observer observer){ observers.remove(observer); } /** * 通知所有注册的观察者对象 */ protected void notifyObservers(){ for (Observer observer: observers){ observer.update(this); } } }
ConcreteSubject
/** * 具体的目标对象,负责吧有关状态存入到相应的观察者对象 * 并在自己状态 */ public class ConcreteSubject extends Subject { /** * 目标对象的状态 */ private String subjectState; public String getSubjectState() { return subjectState; } public void setSubjectState(String subjectState) { this.subjectState = subjectState; this.notifyObservers(); } }
Test
public class Test { public static void main(String[] args) { //观察者 Observer concreteObserver1 = new ConcreteObserver(); Observer concreteObserver2 = new ConcreteObserver(); //目标对象 即被观察者 目标可以有多个,此demo通过 state区分 ConcreteSubject subject1 = new ConcreteSubject(); //注册观察者 subject1.attach(concreteObserver1); subject1.attach(concreteObserver2); // ConcreteSubject subject2 = new ConcreteSubject(); // //注册观察者 // subject2.attach(concreteObserver1); // subject2.attach(concreteObserver2); //发出通知 subject1.setSubjectState("通知1:已经下发了"); // System.out.println("===换一个主题======"); // subject2.setSubjectState("通知2:已经下发了"); } }
运行结果: 主题发一个消息,观察者都能收到:
大话设计模式中看门放哨小案例
Subject
public interface Subject { /** * 添加观察者 * @param observer */ void addObserver(Observer observer); /** * 移除指定的观察者 * @param observer */ void removeObserver(Observer observer); /** * 移除所有的观察者 */ void removeAll(); /** * data 是要通知给观察者的数据 * 因为Object是所有类的父类,可以使用多态,当然 你也可以使用 泛型 * @param data */ void notifyAllObserver(Object data); /** * 单独 通知某一个观察者 * @param observer * @param data * data 是要通知给观察者的数据 * 因为Object是所有类的父类,可以使用多态,当然 你也可以使用 泛型 */ void notify(Observer observer,Object data); }
ConcreteSubject
/** * 具体的主题对象 * 这里就不实现线程安全的功能了, * 有兴趣的话可以参考java.util报下的Observable * @author xujun * */ public class ConcreteSubject implements Subject { List<Observer> mList = new ArrayList<>(); @Override public void addObserver(Observer observer) { // 确保相同的观察者只含有一个 if (observer == null) { throw new NullPointerException("observer == null"); } if (!mList.contains(observer)) { mList.add(observer); } } @Override public void removeObserver(Observer observer) { mList.remove(observer); } @Override public void removeAll() { mList.clear(); } @Override public void notifyAllObserver(Object data) { for (Observer observer : mList) { observer.update(data); } } @Override public void notify(Observer observer, Object data) { if(observer!=null){ observer.update(data); } } }
Observer
/** * 观察者接口 * @author Administrator * */ public interface Observer { /** * * @param data 被观察者传递给观察者的 数据 */ void update(Object data); }
CartoonObserver
public class CartoonObserver implements Observer { @Override public void update(Object data) { System.out.println( " 我是"+this.getClass(). getSimpleName()+", "+data+"别看漫画了"); } }
NBAObserver
public class NBAObserver implements Observer { public class CartoonObserver implements Observer { @Override public void update(Object data) { System.out.println( " 我是"+this.getClass().getSimpleName()+", "+data+"别看漫画了"); } } @Override public void update(Object data) { System.out.println(" 我是" + this.getClass().getSimpleName() + ", " + data + "别看NBA了"); } }
TestObserver
public class TestObserver { public static void main(String[] args) { //主题 ConcreteSubject concreteSubject = new ConcreteSubject(); //观察者 CartoonObserver cartoonObserver = new CartoonObserver(); NBAObserver nbaObserver = new NBAObserver(); //添加观察者 concreteSubject.addObserver(cartoonObserver); concreteSubject.addObserver(nbaObserver); //发布消息通知 concreteSubject.notifyAllObserver("老师来了"); } }
运行结果: 只要放哨的一发通知,观察者就收到了。
三、经典观察者模式的两种使用方式: 推和拉
观察者模式使用时,其实分2个阶段:
-
准备阶段,维护目标和观察者关系的阶段
-
实际运行阶段,也就是目标发生变化,引起观察者做出发应的阶段
这里说的使用方式,针对的是实际运行阶段。获取目标确切数据发起者的问题。
- 推模型
目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,这些数据,是目标对象定义,相当于广播给观察者。刚才的例子中,第2个就是推模型。 - 拉模型
目标对象在通知观察者的时候,只是把自己的引用给观察者,观察者根据需要,使用引用获取。刚才的例子中,第一个例子就是拉模型。目标对象吧this传递给观察者。
开发中如果数据确定,可以用推模型,如果观察者要得到的数据不固定,建议用拉模型,更加灵活,扩展性强。总之,还是拉模型好。
新手一般只学到上面。下面进入入深入思考的部分
:
高级部分(应用场景)
一、 如何让观察者区别对待
上面的demo,均是目前通知观察者的时候全部都通知,根据不同的情况来让不同的观察者处理操作,如何设计呢?
思路
:2种,
一种是目标可以全部通知,但是观察者不做任何操作,
另一种就是在目标里面进行判断,直接不通知了,
这里推荐第二种,可以统一逻辑控制,并进行观察者的统一分派,有利于业务控制和今后的扩展。
来个例子: 水质污染,根据污染情况分别通知检测员,预警人员,检测部门领导。
代如下:
WaterQualitySubject - 水质监测的目标对象
/** * 定义水质监测的目标对象 */ public abstract class WaterQualitySubject { /** * 用来保存注册 */ protected List<WatcherObserver> observers = new ArrayList<>(); /** * 注册观察者对象 */ public void attach(WatcherObserver observer){ observers.add(observer); } /** * 删除观察者对象 */ public void detach(WatcherObserver observer){ observers.remove(observer); } /** * 通知相应的观察者对象 */ public abstract void notifyWathers(); /** * 获取水质污染的级别 */ public abstract int getPolluteLevel(); }
WaterQuality - 具体的水质监测对象
/** * 具体的水质监测对象 */ public class WaterQuality extends WaterQualitySubject { /** * 污染的级别,0表示正常,1表示轻度污染,2表示中度污染,3表示高度污染 */ private int polluteLevel = 0; /** * 获取水质污染的级别 */ @Override public int getPolluteLevel() { return polluteLevel; } public void setPolluteLevel(int polluteLevel) { this.polluteLevel = polluteLevel; this.notifyWathers(); } /** * 通知相应的观察者对象 */ @Override public void notifyWathers() { //循环所在注册的观察者 for (WatcherObserver watcher: observers){ //开始根据污染级别判断是否需要通知,由这里总控 if (this.polluteLevel >=0 ){ //通知监测员做记录 if (("监测人员").equals(watcher.getJob())){ watcher.update(this); } } if (this.polluteLevel >=1 ){ //通知预警人员 if (("预警人员").equals(watcher.getJob())){ watcher.update(this); } } if (this.polluteLevel >=2 ){ //通知监测员部门领导 if (("监测部门领导").equals(watcher.getJob())){ watcher.update(this); } } } } }
WatcherObserver
public interface WatcherObserver { /** * 被通知的方法 * @param subject */ public void update(WaterQualitySubject subject); /** * 设置观察人员的职务 */ public void setJob(String job); /** * 获取观察人员的职务 */ public String getJob(); }
Watcher
public class Watcher implements WatcherObserver{ private String job; @Override public void update(WaterQualitySubject subject) { //这里采用的是拉的方式 System.out.println(job+"获取到通知,当前污染级别为:" + subject.getPolluteLevel()); } @Override public void setJob(String job) { this.job = job; } @Override public String getJob() { return this.job; } }
Test
public class Test { public static void main(String[] args) { //创建水质主题对象 WaterQuality subject = new WaterQuality(); //创建几个观察者 WatcherObserver watcher1 = new Watcher(); watcher1.setJob("监测人员"); WatcherObserver watcher2 = new Watcher(); watcher2.setJob("预警人员"); WatcherObserver watcher3 = new Watcher(); watcher3.setJob("监测部门领导"); //注册观察者 subject.attach(watcher1); subject.attach(watcher2); subject.attach(watcher3); //填写水质报告 System.out.println("当水质为正常的时候-----------"); subject.setPolluteLevel(0); System.out.println("当水质为轻度污染的时候-----------"); subject.setPolluteLevel(1); System.out.println("当水质为中度污染的时候-----------"); subject.setPolluteLevel(2); } }
运行结果:
可以看到根据预警级别通知了不同的观察者。
主要逻辑在目标对象实现类上,在if的判断上是比较取巧的写法,不是if 。。else if … .,而是多个if。组合if,好好体会学习,以后借鉴使用下。
二、如何不依赖抽象观察者,也能实现观察者模式:
上面的例子,都是有个抽象观察者的角色的,目标对象直接操作抽象观察者。如果不想使用抽象观察者,考虑的思路如下:
- 1.我们新建抽象观察者类,别人给提供,我们实现就好了,这里我们可以
使用java自带的观察者模式
,他已经帮我们自动提供了抽象观察者,和抽象目标类,我们按照他的规则直接使用就可以了。 - 这里一会展示 使用java自带的观察者模式的例子:
- 2.注册观察者和通知观察者的工作交给一个第三方,解耦目标和观察者。
这种实现的方式很多,主要思路是反射委托
。
这里一会展示4个例子:
- 反射委托实现上面第2个例子,上课看nba和动漫,老师来了,做出不同反应的例子。
- 比如:模仿swing组件实现原理的例子(
这个例子很特别,观察者可以观察多个目标对象
) - spring中listener实现观察者模式的例子
- springboot使用Guava框架提供的eventBus,实现事件处理的例子。
1.使用java自带的观察者模式的例子
java提供抽象观察者Observer,抽象目标对象Observable,通知观察者的方法名必须是update。通知前必须调动setChange()方法,具体代码如下:
具体目标类(被观察者)
/** * Title: GPer * Description: JDK提供的一种观察者的实现方式,被观察者 * * @author hfl * @version V1.0 * @date 2020-06-03 */ public class GPer extends Observable { private String name = "GPer生态圈"; private static GPer gper = null; private GPer() { } public static GPer getInstance(){ if(null == gper){ gper = new GPer(); } return gper; } public void publishQuestion(Question question){ System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。"); setChanged(); notifyObservers(question); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
来个辅助的类,和观察者模式没啥关系的业务类:Question
/** * Title: Question * Description: TODO * * @author hfl * @version V1.0 * @date 2020-06-03 */ public class Question { //提问者 private String userName; //提问问题 private String content; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
具体的观察者类:Teacher
public class Teacher implements Observer { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Teacher(String name) { this.name = name; } @Override public void update(Observable o, Object arg) { GPer gper = (GPer)o; Question question = (Question)arg; System.out.println("==============================="); System.out.println(name + "老师,你好!\n" + "您收到了一个来自“" + gper.getName() + "”的提问,希望您解答,问题内容如下:\n" + question.getContent() + "\n" + "提问者:" + question.getUserName()); } }
测试类:
public class ObserverTest { public static void main(String[] args) { GPer gper = GPer.getInstance(); Teacher tom = new Teacher("Tom"); Teacher mic = new Teacher("Mic"); //这为没有@Tom老师 Question question = new Question(); question.setUserName("小明"); question.setContent("观察者设计模式适用于哪些场景?"); gper.addObserver(tom); gper.addObserver(mic); gper.publishQuestion(question); } }
运行结果:
查看结果,完美的展示了目标对象通知所有观察者的实现。
2.反射委托实现上面第2个例子,学生根据老师到来,做出不同反应的例子。
具体代码:
/** * 事件对象的封装类 * * @author Administrator */ public class Event { //要执行方法的对象 private Object object; //要执行的方法名称 private String methodName; //要执行方法的参数 private Object[] params; //要执行方法的参数类型 private Class[] paramTypes; public Event() { } public Event(Object object, String methodName, Object... args) { this.object = object; this.methodName = methodName; this.params = args; contractParamTypes(this.params); } //根据参数数组生成参数类型数组 private void contractParamTypes(Object[] params) { this.paramTypes = new Class[params.length]; for (int i = 0; i < params.length; i++) { this.paramTypes[i] = params[i].getClass(); } } public Object getObject() { return object; } //这里省略了若干get和set方法 /** * 根据该对象的方法名,方法参数,利用反射机制,执行该方法 * * @throws Exception */ public void invoke() throws Exception { Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes()); if (null == method) { return; } method.invoke(this.getObject(), this.getParams()); } public void setObject(Object object) { this.object = object; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Object[] getParams() { return params; } public void setParams(Object[] params) { this.params = params; } public Class[] getParamTypes() { return paramTypes; } public void setParamTypes(Class[] paramTypes) { this.paramTypes = paramTypes; } }
EventHandler
/** * Title: EventHandler * Description: 事件的 处理者 * * @author hfl * @version V1.0 * @date 2020-06-05 */ public class EventHandler { //是用一个List private List<Event> objects; public EventHandler(){ objects=new ArrayList<Event>(); } //添加某个对象要执行的事件,及需要的参数 public void addEvent(Object object,String methodName,Object...args){ objects.add(new Event(object,methodName,args)); } //通知所有的对象执行指定的事件 public void notifyX() throws Exception{ for(Event e : objects){ e.invoke(); } } }
通知者的 抽象类Notifier
public abstract class Notifier { private EventHandler eventHandler = new EventHandler(); public EventHandler getEventHandler() { return eventHandler; } public void setEventHandler(EventHandler eventHandler) { this.eventHandler = eventHandler; } /** * 增加需要帮忙 放哨 的 学生 * * @param object 要执行方法的对象 * @param methodName 执行方法 的方法名 * @param args 执行方法的参数 */ public abstract void addListener(Object object, String methodName, Object... args); /** * 告诉所有要帮忙放哨的学生:老师来了 */ public abstract void notifyX(); }
通知者 GoodNotifier
public class GoodNotifier extends Notifier { @Override public void addListener(Object object, String methodName, Object... args) { System.out.println("有新的同学委托尽职尽责的放哨人!"); EventHandler handler = this.getEventHandler(); handler.addEvent(object, methodName, args); } @Override public void notifyX() { System.out.println("尽职尽责的放哨人告诉所有需要帮忙的同学:老师来了"); try{ this.getEventHandler().notifyX(); }catch(Exception e){ e.printStackTrace(); } }
WatchCartoonListener
/** * Title: WatchCartoonListener * Description: 具体监听者(观察者) * * @author hfl * @version V1.0 * @date 2020-06-05 */ public class WatchCartoonListener extends GoodNotifier { public WatchCartoonListener() { System.out.println("WatchCartoonListener 我正在看漫画,开始时间:"+ LocalDateTime.now().toString()); } public void stopPlayingGame(Date date){ System.out.println("WatchCartoonListener 停止看漫画了,结束时间:"+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date)); } }
WatchingNBAListener
public class WatchingNBAListener extends GoodNotifier { public WatchingNBAListener() { System.out.println("WatchingNBAListener我正在看NBA,开始时间是: " + LocalDateTime.now().toString()); } public void stopWatchingTV(Date date){ System.out.println("WatchingNBAListener 快关闭NBA直播 , 结束时间是:" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date)); } }
测试类:
public class Test { public static void main(String[] args) { //创建一个尽职尽责的放哨者 Notifier goodNotifier = new GoodNotifier(); //创建一个玩游戏的同学,开始玩游戏 WatchCartoonListener playingGameListener = new WatchCartoonListener(); //创建一个看电视的同学,开始看电视 WatchingNBAListener watchingTVListener = new WatchingNBAListener(); //玩游戏的同学告诉放哨的同学,老师来了告诉一下 goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date()); //看电视的同学告诉放哨的同学,老师来了告诉一下 goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date()); try { //一点时间后 Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } //老师出现,放哨的人通知所有要帮忙的同学:老师来了 goodNotifier.notifyX(); } }
运行结果:
public class Test { public static void main(String[] args) { //创建一个尽职尽责的放哨者 Notifier goodNotifier = new GoodNotifier(); //创建一个玩游戏的同学,开始玩游戏 WatchCartoonListener playingGameListener = new WatchCartoonListener(); //创建一个看电视的同学,开始看电视 WatchingNBAListener watchingTVListener = new WatchingNBAListener(); //玩游戏的同学告诉放哨的同学,老师来了告诉一下 goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date()); //看电视的同学告诉放哨的同学,老师来了告诉一下 goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date()); try { //一点时间后 Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } //老师出现,放哨的人通知所有要帮忙的同学:老师来了 goodNotifier.notifyX(); } }
运行结果:
事件委托机制 分析
1.放哨者完全不知道做游戏者的存在,完全解耦。(当然,功劳归功于Event和EventHandler,且这两个类具有通用性)
2.老师来了后游戏者停止游戏回到座位,看NBA者停止看NBA,看漫画这停止看漫画,玩游戏这停止玩游戏。(一次通知,执行了不同类的不同方法)
3.扩展性很高,再来一个打篮球的学生就先写个打篮球学生类,并在测试代码中告诉放哨者一下就好,放哨者完全没有变。重用性好
看了这个例子,再来了解swing组件监听的例子就简单多了,因为原理完全一样,都是有事件和事件处理者管理和调用观察者的方法。
3.swing组件监听的例子
具体看下代码:
这个例子比较强大,既体现了委托注册观察者,又有某个观察者的方法对应某个目标类的具体方法,方法到方法的对应。好好理解,学习,争取项目中使用下:
Event
public class Event { //事件源,事件是由谁发起的保存起来 private Object source; //事件触发,要通知谁 private Object target; //事件触发,要做什么动作,回调 private Method callback; //事件的名称,触发的是什么事件 private String trigger; //事件触发的时间 private long time; public Event(Object target, Method callback) { this.target = target; this.callback = callback; } public Event setSource(Object source) { this.source = source; return this; } public Event setTime(long time) { this.time = time; return this; } public Event setTrigger(String trigger) { this.trigger = trigger; return this; } public Object getSource() { return source; } public long getTime() { return time; } public Object getTarget() { return target; } public Method getCallback() { return callback; } @Override public String toString() { return "Event{" + "\n" + "\tsource=" + source.getClass() + ",\n" + "\ttarget=" + target.getClass() + ",\n" + "\tcallback=" + callback + ",\n" + "\ttrigger='" + trigger + "',\n" + "\ttime=" + time + "'\n" + '}'; } }
EventLisenter
public class EventLisenter { //JDK底层的Lisenter通常也是这样来设计的 protected Map<String, Event> events = new HashMap<String, Event>(); //事件名称和一个目标对象来触发事件 public void addLisenter(String eventType, Object target) { try { this.addLisenter( eventType, target, target.getClass().getMethod("on" + toUpperFirstCase(eventType), Event.class)); } catch (Exception e) { e.printStackTrace(); } } public void addLisenter(String eventType, Object target, Method callback) { //注册事件 events.put(eventType, new Event(target, callback)); } //触发,只要有动作就触发 private void trigger(Event event) { event.setSource(this); event.setTime(System.currentTimeMillis()); try { //发起回调 if (event.getCallback() != null) { //用反射调用它的回调函数 event.getCallback().invoke(event.getTarget(), event); } } catch (Exception e) { e.printStackTrace(); } } //事件名称触发 protected void trigger(String trigger) { if (!this.events.containsKey(trigger)) { return; } trigger(this.events.get(trigger).setTrigger(trigger)); } //逻辑处理的私有方法,首字母大写 private String toUpperFirstCase(String str) { char[] chars = str.toCharArray(); chars[0] -= 32; return String.valueOf(chars); } }
Mouse
public class Mouse extends EventLisenter { public void click(){ System.out.println("调用单击方法"); this.trigger(MouseEventType.ON_CLICK); } public void doubleClick(){ System.out.println("调用双击方法"); this.trigger(MouseEventType.ON_DOUBLE_CLICK); } public void up(){ System.out.println("调用弹起方法"); this.trigger(MouseEventType.ON_UP); } public void down(){ System.out.println("调用按下方法"); this.trigger(MouseEventType.ON_DOWN); } public void move(){ System.out.println("调用移动方法"); this.trigger(MouseEventType.ON_MOVE); } public void wheel(){ System.out.println("调用滚动方法"); this.trigger(MouseEventType.ON_WHEEL); } public void over(){ System.out.println("调用悬停方法"); this.trigger(MouseEventType.ON_OVER); } public void blur(){ System.out.println("调用获焦方法"); this.trigger(MouseEventType.ON_BLUR); } public void focus(){ System.out.println("调用失焦方法"); this.trigger(MouseEventType.ON_FOCUS); } }
MouseEventCallback
/** * 自己写的逻辑,用于回调 * Created by Tom. */ public class MouseEventCallback { public void onClick(Event e){ System.out.println("===========触发鼠标单击事件==========" + "\n" + e); } public void onDoubleClick(Event e){ System.out.println("===========触发鼠标双击事件==========" + "\n" + e); } public void onUp(Event e){ System.out.println("===========触发鼠标弹起事件==========" + "\n" + e); } public void onDown(Event e){ System.out.println("===========触发鼠标按下事件==========" + "\n" + e); } public void onMove(Event e){ System.out.println("===========触发鼠标移动事件==========" + "\n" + e); } public void onWheel(Event e){ System.out.println("===========触发鼠标滚动事件==========" + "\n" + e); } public void onOver(Event e){ System.out.println("===========触发鼠标悬停事件==========" + "\n" + e); } public void onBlur(Event e){ System.out.println("===========触发鼠标失焦事件==========" + "\n" + e); } public void onFocus(Event e){ System.out.println("===========触发鼠标获焦事件==========" + "\n" + e); } }
MouseEventType
public interface MouseEventType { //单击 String ON_CLICK = "click"; //双击 String ON_DOUBLE_CLICK = "doubleClick"; //弹起 String ON_UP = "up"; //按下 String ON_DOWN = "down"; //移动 String ON_MOVE = "move"; //滚动 String ON_WHEEL = "wheel"; //悬停 String ON_OVER = "over"; //失焦 String ON_BLUR = "blur"; //获焦 String ON_FOCUS = "focus"; }
MouseEventTest
public class MouseEventTest { public static void main(String[] args) { MouseEventCallback callback = new MouseEventCallback(); Mouse mouse = new Mouse(); //@谁? @回调方法 mouse.addLisenter(MouseEventType.ON_CLICK,callback); mouse.addLisenter(MouseEventType.ON_FOCUS,callback); mouse.click(); mouse.focus(); } }
public class Keybord extends EventLisenter { public void down(){ } public void up(){ } }
运行结果:
4.spring中listener实现观察者模式的例子
Spring的事件机制用到了观察者模式。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
在Spring中各个listener相当于观察者。event事件相当于目标,观察者,而容器用来管理和注册观察者,发布事件。
具体的演示代码如下:
事件类:OrderEvent 相当于目标类
public class OrderEvent extends ApplicationEvent { public OrderEvent(Object source) { super(source); } }
OrderSmsListener相当于观察者
@Component public class OrderSmsListener implements ApplicationListener<OrderEvent> { @Override public void onApplicationEvent(OrderEvent orderEvent) { System.out.println("orderSmsListener receive event from " + orderEvent.getSource()); } }
业务类:
@Service public class OrderService { @Autowired private ApplicationContext applicationContext; public void order() { applicationContext.publishEvent(new OrderEvent("orderService")); } }
另外spring容器相当于委托类。
测试:
@RunWith(SpringRunner.class) @SpringBootTest public class PatternApplicationTest { @Autowired private OrderService orderService; @Test public void contextLoads() { } @Test public void testOrder() { orderService.order(); } }
运行结果:
显示,事件发布后,监听这就能观察到这个事件了,可以做更多的操作。
4.springboot中集成谷歌的Guava,通过eventBus实现订阅发布功能
EventBus无需实现复杂的事件、监听者、发布者。前面讲的是面向类,现在:Guava是面向是方法,更加强大。能够轻松落地观察模式的一种解决方案。
演示个例子:
引入Guava的包:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>20.0</version> </dependency>
EventBus 事件总线
//api封装 public class MyEventBus { /** 事件任务总线 */ private final static EventBus tiemEventBus = new EventBus(); /** * 触发同步事件 * * @param event */ public static void post(Object event) { tiemEventBus.post(event); } /** * 注册事件处理器 * * @param handler */ public static void register(Object handler) { tiemEventBus.register(handler); } /** * 注销事件处理器 * * @param handler */ public static void unregister(Object handler) { tiemEventBus.unregister(handler); } }
消息实体类
public class Message { private MessageType messageType; private String messageContent; public Message(MessageType messageType, String messageContent) { this.messageType = messageType; this.messageContent = messageContent; } public MessageType getMessageType() { return messageType; } public void setMessageType(MessageType messageType) { this.messageType = messageType; } public String getMessageContent() { return messageContent; } public void setMessageContent(String messageContent) { this.messageContent = messageContent; } public enum MessageType { OPENDOOR(1, "openDoor"), CLOSEDOOR(2,"closeDoor"); private int code; private String value; MessageType(int code, String value) { this.code = code; this.value = value; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } }
事件监听者
@Component abstract class MyApplicationListener implements ApplicationListener<ApplicationPreparedEvent> { /** * ApplicationPreparedEvent 上下文准备事件 * @param applicationPreparedEvent */ @Override public void onApplicationEvent(ApplicationPreparedEvent applicationPreparedEvent) { ConfigurableApplicationContext applicationContext = applicationPreparedEvent.getApplicationContext(); MyApplicationListener bean = applicationContext.getBean(this.getClass()); System.out.println("regist listener to eventBus...."+bean); MyEventBus.register(bean); } }
订阅者(也即监听者)继承至MyApplicationListener。
@Component public class MyListentenerSubscribe extends MyApplicationListener{ @Subscribe public void on(Message message){ System.out.println("subscribe message-> messgeType:"+message.getMessageType()+"\n messageContent:"+message.getMessageContent()); } }
测试:
@RestController public class EventPublishCtrl extends LogBase { @GetMapping("/publish") public void publishEvent() { log.info("this publish method..."); MyEventBus.post(new Message(Message.MessageType.OPENDOOR,"芝麻开门!")); } }
启动项目,然后调用发布方法:
订阅者收到具体的消息类型,以及消息内容。
三、观察者模式的本质
上面主要介绍了观察者模式的基本经典结构;入门小demo;使用的2种方式;观察者模式变形写法;java中封装好的观察者模式使用方式;不依赖抽象观察者的方式,比如使用反射委托例子,swing使用观察者的例子,spring中listener观察者模式实现的例子,springboot 的eventBus的观察者模式的例子. 最后说下观察者模式的本质。
自此,观察者模式总算写完了,其实后续可能会有加上mq的例子,还没深入学习,先这样吧。
参考文章:
https://blog.csdn.net/gdutxiaoxu/article/details/51824769
https://blog.csdn.net/fw19940314/article/details/100010397
https://www.cnblogs.com/wkzhao/p/10229283.html
完
这篇关于高手眼中的观察者模式有什么不一样的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-06小米11i印度快充版ROM合集:极致体验,超越期待
- 2024-10-06【ROM下载】小米11i 5G 印度版系统, 疾速跃迁,定义新速度
- 2024-10-06【ROM下载】小米 11 青春活力版,青春无极限,活力全开
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求