一,类的四大基本关系: 

a.关联关系:如A类调用B类。 

b.继承关系:如A类是B类的父类。 

c.聚合关系:如装橘子的箱子,箱子是否存在与里面装没装橘子没有任何关系,也就是说橘子不会影响箱子的存在。 

d.组合关系:如一个小组,小组是否存在与小组中是否有组员是息息相关的,如果没有组员,小组就不存在了。

监听器模式:事件源经过事件的封装传给监听器,当事件源触发事件后,监听器接收到事件对象可以回调事件的方法

观察者模式:观察者(Observer)相当于事件监听者,被观察者(Observable)相当于事件源和事件,执行逻辑时通知observer即可触发oberver的update,同时可传被观察者和参数

二,监听模式

   当事件源对象上发生操作时,将会调用事件监听器的一个方法,并在调用该方法时把事件对象传递过去。

观察者模式和监听器

 

三,监听模式的使用实例

观察者模式和监听器

 

 四,观察者模式与监听模式的区别

       什么也不说了,图是最直观的

 观察者模式和监听器

1. 事件-监听机制 
事件源经过事件的封装传给监听器,当事件源触发事件后,监听器接收到事件对象可以回调事件的方法。 
观察者模式和监听器

2. 观察者模式 
观察者(Observer)相当于事件监听者(监听器),被观察者(Observable)相当于事件源和事件,执行逻辑时通知observer即可触发oberver的update,同时可传被观察者和参数。简化了事件-监听模式的实现。 
观察者模式和监听器

3.对比 

(1) 从uml图上也可以看出,Observer的实现相对简单,event-listener需要实现三个角色,observer-observable需要实现两个角色。 

(2)Observable的api已经把对观察者的注册,删除等定义好了,而且是线程安全的。而event-listener需要使用者自己实现。 

(3)两者都需要自己定义并实现触发事件的通知。但Observable需要注意要在通知Observer之前调用jdk提供的setChanged()。 

(4)event-listener是传统的c/s界面事件模型,分事件源和事件(状态)角色,事件源要经过事件的包装、成为事件的属性之一再传递给事件监听/处理者,这个事件监听者就相当于观察者。Observer更简洁一些。两者在思想上是统一的,很多框架仍然使用了event-listener模式,比如spring框架的ApplicationEvent,ApplicationListener。



为了实现多个模块之间的联动,最好的方法是使用观察者模式。网上介绍的资料也比较多,今天我就从另一个方面谈谈自己对观察者模式的理解。从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属于事件驱动或方法驱动。例如按钮的例子,用户造成了某个事件,立刻触发后台程序的响应。这里的关键词是响应。


相关文章: