为了实现多个模块之间的联动,最好的方法是使用观察者模式。网上介绍的资料也比较多,今天我就从另一个方面谈谈自己对观察者模式的理解。从JDK提供的支持库里,我们能够找到四个对象:Observable、Observer、EventListener、EventObject。
先模拟一个后台处理过程:
import java.util.Observable; public class Subject extends Observable {
private int value; public int getValue() {
return value;
} private void progress() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = i;
setChanged(); // 值发生改变
notifyObservers(); // 调用所有注册的观察者
}
} public void onStart() {
progress();
} }
稍微对上面的代码做一些解释,顺便介绍一下Observable这个类:
顾名思义“能够被观察的对象”,用户能够继承它并增加自己的定义,它提供了几个方法。
addObserver(Observer o):注册观察者,这个方法的内部其实就是提供了一个队列。将多有观察者储存在队列中,当有事件发生的时候遍历这个队列。
hasChanged()
与setChanged():当事件发生时,需要调用
setChanged()
方法,此时hasChanged()
返回true否则返回false。
notifyObservers
()
与
notifyObservers
(
Object
arg)
:通知所有观察者可以获取各自需要的变量或只推送某个对象。
下面模拟一个进度条:
public class Progress implements Observer {
private int value; @Override
public void update(Observable o, Object arg) {
value = ((Subject)o).getValue();
System.out.print("#");
} public static void main(String[] args) {
Progress ui = new Progress();
Subject subject = new Subject();
subject.addObserver(ui);
subject.onStart();
}
}
进度条作为后台程序的观察者需要实现update(Observable o, Object arg)方法。当Subject调用setChanged()后使用notifyObservers()方法会回调update。需要注意,notifyObservers方法隐式调用clearChanged(),因此如果用户试图在update方法中调用o.hasChanged()的时候,只会返回false。
我们再来看一个使用监听器的例子。相对上面的代码来说,实现自己的监听器会稍微复杂一些。但是只要我们先想清楚逻辑就不会有什么难度。
监听器模式将对象分为了三个模块:Source(事件源)、ChangeEvent(事件)、StatusListener(监听器)。事件源可能会触发多个事件,针对不同的事件都可以定义独立的监听器,当事件源触发事件的时候监听器获得通知并实现用户自定义的业务逻辑。相比Observer和Observable的二元实现来说。监听器抽象了事件对象,目的是不同的事件源可能包含相同的触发事件,为了提供更好的内聚处理。监听器的处理逻辑是针对事件本身而言的。
那么也许你会好奇,如果监听的对象是事件本身如何根据不同的事件源提供不同的逻辑呢?秘诀在于事件本身会提供一个事件源对象供监听器判断。
或许现在你反而会更加困惑。不要紧其实当我第一次自己去实现监听器的时候面对这三个模块的逻辑也相当混乱,等你看完代码以后再回来仔细推敲上面的文字会豁然开朗。
先看一下事件源:
public class Source {
private List<StatusListener> statusListeners = new ArrayList<>(); public void addStatusListener(StatusListener listener) {
statusListeners.add(listener);
} public void onClick() {
ChangeEvent event = new ChangeEvent(this);
event.setStatus("click");
notifyListener(event);
} public void onDoubleClick() {
ChangeEvent event = new ChangeEvent(this);
event.setStatus("doubleClick");
notifyListener(event);
} public void onMove() {
ChangeEvent event = new ChangeEvent(this);
event.setStatus("move");
notifyListener(event);
} private void notifyListener(ChangeEvent event) {
Iterator<StatusListener> it = statusListeners.iterator();
while(it.hasNext()) {
StatusListener listener = it.next();
listener.changeStatus(event);
}
}
}
事件源提供了三个触发事件,分别是点击(click)、双击(doubleclick)、拖拽(move)。这是设计UI的时候经常遇到的三个事件,相信大家不应该陌生。Source没有继承任何接口或父类,完全是一个自定义的类。那么针对三种触发类型,我们接下来需要提供事件。
public class ChangeEvent extends EventObject {
private String status; public ChangeEvent(Object source) {
super(source);
} public String getStatus() {
return status;
} public void setStatus(String status) {
this.status = status;
} }
ChangeEvent对象继承EventObject,必须提供一个注册事件源的构造方法。然后在事件中,我们定义了一个字符串变量作为保存状态的接口,更加复杂的逻辑原理也是一样的。
定义好事件以后,我们再定义一个监听器接口。
public interface StatusListener extends EventListener {
void changeStatus(ChangeEvent event);
}
EventListener接口仅仅是一个标志性接口,内部没有做任何处理。所有逻辑都由使用者自己实现。我们就定义一个changeStatus方法,要求提供ChangeEvent作为参数。
最后返回Source类,添加一个测试用的main方法
public static void main(String[] args) {
Source source = new Source();
source.addStatusListener(new StatusListener(){ @Override
public void changeStatus(ChangeEvent event) {
System.out.println(event.getStatus());
}
});
source.onClick();
source.onDoubleClick();
source.onMove();
}
在main方法中我们能够观察到一些区别,首先,监听器并没有提供默认的addListener方法。我们需要自己创建一个保存所有监听对象的队列。
其次,也没有提供notifyListener方法,为了触发监听器我们也需要自己实现遍历队列的逻辑。
最后说一下我对以上两种模式区别的理解。
Observable和Observer属于对象驱动或值驱动。例如进度条的例子,UI界面需要时刻观察后台进度的变化从而动态更新自己。这里的关键词是动态更新。
EventListener和EventObject属于事件驱动或方法驱动。例如按钮的例子,用户造成了某个事件,立刻触发后台程序的响应。这里的关键词是响应。
Java设计模式(三)——观察者模式和监听器的更多相关文章
-
理解java设计模式之观察者模式
在生活实际中,我们经常会遇到关注一个事物数据变化的情况,例如生活中的温度记录仪,当温度变化时,我们观察它温度变化的曲线,温度记录日志等.对于这一类问题,很接近java设计模式里面的“观察者模式”,它适 ...
-
java设计模式之观察者模式以及在java中作用
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependen ...
-
java设计模式02观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 这里主要讲一下学习内置观察者的记录,在JA ...
-
java设计模式之观察者模式
观察者模式 观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式.模型-视图(View)模式.源-收听者(Listener)模式或从属者模式)是软件设计模式的一种.在此种模 ...
-
JAVA设计模式 之 观察者模式
简介: 观察者模式是JDK中最多的设计模式之一,非常有用,观察者模式介绍了一对多的依赖关系及松耦合,有了观察者,你将会消息灵通. 认识观察者模式,看一个报纸.杂志订阅是怎么回事: (1). 报社的业务 ...
-
折腾Java设计模式之观察者模式
观察者模式 Define a one-to-many dependency between objects where a state change in one object results in ...
-
JAVA设计模式之观察者模式 - Observer
有趣的事情发生时,可千万别错过了!有一个模式可以帮你的对象知悉现况,不会错过该对象感兴趣的事.对象甚至在运行时可决定是否要继续被通知.有了观察者,你将会消息灵通. 介绍 观察者模式的定义: 在对象之间 ...
-
JAVA设计模式 之 观察者模式(JDK内置实现)
简介:使用JAVA内置的帮你搞定观察者模式. 1. 先把类图放在这里: (1). Observable类追踪所有的观察者,并通知他们. (2). Observer这个接口看起来很熟悉,它和我们之前写的 ...
-
java设计模式之观察者模式(9)
Java观察者模式的浅析 简单地说,观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监察一个主题对象.这样一个主题对象在状态上的变化能够通知所有的依赖于此对象的那些观察者对象,使这些观察者 ...
-
java设计模式:观察者模式
package Observer; public class Test { /** * client测试类别 * 观察者模式一般由四部分组成: * 1摘要观察员(教科书被称为一般"Subje ...
随机推荐
-
让你的Android程序更省电
app主要耗电的原因如下: 1 cpu频繁的运转 -----控制线程 2 大数据量的传输----- 数据压缩传输 3 不停的在网络间切换------------判断网络状体 4 人开发的程序后台都 ...
-
一个很酷的加载loading效果--IT蓝豹
一个很酷的加载loading效果,自定义LeafLoadingView实现,LeafLoadingView继承view, 本例子主要由以下几点构成 (1):RotateAnimation实现叶子旋转 ...
-
@property和@synthesize的特性
基础回顾:get方法和set方法 定义类成员变量时,可以在@interface中定义,也可以在@implementation中定义: 在@interface中声明,成员变量的状态是受保护的,即“@pr ...
-
初学Android开发怎么开始?
在几个Android开发者群里面总是碰到新人提出这样的问题:"我想学Android开发,但是不知道怎么开始,不知道要学一些什么." 其实,我在最开始学习Android开发的时候也 ...
-
hdu 2019
Problem Description 有n(n<=100)个整数,已经按照从小到大顺序排列好,现在另外给一个整数x,请将该数插入到序列中,并使新的序列仍然有序. Input 输入数据包含多 ...
-
Android项目包装apk和apk反编译,xml反编译
一.项目和一般原则其不足之处包 (1)开发一个简单的项目.当发布了APK档.假设我们不使用签名的方式,直接地bin文件夹中找到*.apk档.非常方便,但是,当我们在使用的用户,可能有其他方案覆盖安装. ...
-
【G】开源的分布式部署解决方案(三) - 一期规划定稿与初步剖析
G.系列导航 [G]开源的分布式部署解决方案 - 预告篇 [G]开源的分布式部署解决方案(一) - 开篇 [G]开源的分布式部署解决方案(二) - 好项目是从烂项目基础上重构出来的 [G]开源的分布式 ...
-
vue-常用指令
一.一些指令 1.v-bind 绑定元素(简写 :) <div id="app-2"> <span v-bind:title="message" ...
-
Integer与int区别
Integer与int的区别:估计大多数人只会说道两点,一开始我也不太清楚,Ingeter是int的包装类,int的初值为0,Ingeter的初值为null.但是如果面试官再问一下Integer i ...
-
【转载】 5G+边缘计算,着眼可见的未来 【边缘计算】
原文地址: https://www.cnblogs.com/upyun/p/10641489.html ------------------------------------------------ ...