高手眼中的观察者模式有什么不一样
2020-06-05 16:07:42来源:博客园 阅读 ()
高手眼中的观察者模式有什么不一样
观察者模式,是使用很多的一种模式,初次了解,只是根据入门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
个人微信公众号:
搜索: 怒放de每一天
不定时推送相关文章,期待和大家一起成长!!
完
谢谢大家支持!
原文链接:https://www.cnblogs.com/hufanglei/p/13052992.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- MyBatis中的$和#,用不好,准备走人! 2020-06-11
- 构造函数中的this()和super() 2020-06-10
- 怎么用Java 高效提取、替换、删除PDF文档中的图片 2020-06-09
- Spring12_Spring中的事务控制 2020-06-07
- 深入理解:设计模式中的七大设计原则 2020-06-07
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash