设计模式---观察者模式(Observer)和委托事件模型(DEM)

1 引言

观察者模式,又名发布订阅模式,是一个一对多的关系,当被观察者发生某种变化,对应其观察者做出相应的改变。比如说,某学校研究生实验室有2个学生,2个学生某个上午在实验室,A在玩游戏,B在看电影,但是害怕老板进屋把他们逮个正着,怎么办?这2个学生和楼下安保处的门卫关系都特别好,他们就和门卫说了,如果看到他们老板进楼,就赶紧通知他们一声,他们好转换进入学习模式。在这个例子中,门卫就是被观察者(可不是老板啊),也就是事件的发布者,2个学生就是观察者,只要在门卫那里登了记,门卫一群发消息,就能收到通知,做出改变。

2 观察者模式的优点

解耦。观察者模式所做的工作就是解除耦合,让观察者与被观察者都依赖于各自上层的抽象,而不是依赖于具体实现,从而使得观察者与被观察者的具体实现代码变化不会互相影响。

3 适用场景

当一个对象的改变需要引起其他对象的改变时,这里是指状态的变化,而不是指代码的变化。尤其是一个对象变化但却不知道会引起多少其他对象的变化。

4 观察者代码示例

4.1 抽象的被观察者

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 10:47
 * @description: 通知者的接口
 **/

public interface Subject {

    /**
     * 添加观察者
     *
     * @param observer 观察者
     */
    void addObserver(Observer observer);

    /**
     * 删除观察者
     *
     * @param observer 观察者
     */
    void deleteObserver(Observer observer);

    /**
     * 通知观察者
     *
     * @param msg 通知信息
     */
    void notifyAll(String msg);
}

4.2 具体的观察者

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 11:02
 **/

public class ConcreteSubject implements Subject{

    //确保观察者不会重复存在
    Set<Observer> observers = new HashSet<>();

    @Override
    public void addObserver(Observer observer) {
        if (observer == null){
            throw new NullPointerException("观察者不允许为空");
        }
        observers.add(observer);
    }

    @Override
    public void deleteObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyAll(String msg) {
        for (Observer o: observers) {
            o.update(this, msg);
        }
    }
}

4.3 抽象的观察者

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 10:47
 * @description: 观察者的接口
 **/

public interface Observer {


    /**
     * 接收到通知,更新自我的状态
     *
     * @param subject 通知者
     * @param msg 通知信息
     */
    void update(Subject subject, String msg);
}

4.4 具体观察者1-玩游戏

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 11:09
 **/

public class GameObserver implements Observer{
    @Override
    public void update(Subject subject, String msg) {
        System.out.println("这里是" + subject.getClass() + "," + msg + "请停止打游戏");
    }
}

4.5 具体观察者2-看电影

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 11:09
 **/

public class MovieObserver implements Observer{
    @Override
    public void update(Subject subject, String msg) {
        System.out.println("这里是" + subject.getClass() + "," + msg + "请停止看电影");
    }
}

4.6 主程序

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 11:18
 **/
public class MainClass {
    public static void main(String[] args) throws Exception {
        ConcreteSubject concreteSubject = new ConcreteSubject();
        GameObserver gameObserver = new GameObserver();
        MovieObserver movieObserver = new MovieObserver();

        concreteSubject.addObserver(gameObserver);
        concreteSubject.addObserver(movieObserver);

        concreteSubject.notifyAll("有内鬼");
    }
}

4.7 执行结果

5 观察者模式的不足

1.尽管观察者与被观察者都做了抽象,但是接口之间彼此还是有耦合;例如,我们在定义Subject的接口是,添加观察者与删除观察者还是耦合了观察者Observer接口。

2.被观察者发布的方法是固定不变的,玩游戏的同学收到通知想要做的是关闭游戏,然后在老师来之前开溜,看电影的同学想要在老师来之前关闭电影,打开idea刷个好印象,现在是不能实现的,被观察者统一调用update方法。

6 委托事件模型(DEM)

个人理解,委托,就是把以前自己需要做的事情,转给别人做。主题(Subject)角色负责发布事件,而观察者向特定的主题订阅它所感兴趣的事件,当一个具体主题产生一个事件时,他就会通知所有感兴趣的订阅者。而且能够动态的订阅和取消。DEM中发布者叫做事件源(event source),而订阅者叫做事件监听器(event listener)。

具体如何实现了?我们首先定义一个事件Event,Event的任务就是执行传入进来的对象的方法,这样,对象不同,方法可以不同,这就解决了缺点的第二个问题-观察者固定不变的方法。再定义一个EventHandler类,负责添加事件与发布事件,而Subject只需要传入EventHandler类,添加订阅的事件与发布消息,这样就解决了第一个关于耦合的问题。

7 委托事件代码实现

7.1 Event类

**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 14:14
 **/
@Data
@Setter
@Getter
public class Event {

    /**
     * 委托事件者
     */
    private Object object;

    /**
     * 委托执行的方法
     */
    private String methodName;

    /**
     * 委托执行的方法参数
     */
    private Object[] params;

    /**
     * 执行方法的参数类型
     */
    private Class[] paramTypes;

    public Event(){
        //无参构造
    }
    public Event(Object object, String methodName, Object...params){
        this.object = object;
        this.methodName = methodName;
        this.params = params;
        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 void invoke() throws Exception {
        Method method = object.getClass().getMethod(this.methodName, this.paramTypes);
        method.invoke(this.object, this.params);
    }

}

7.2 EventHandler类


/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 14:37
 **/
@Data
public class EventHandler {

    private Set<Event> events;

    public EventHandler(){
        this.events = new HashSet<>();
    }

     //相当于添加一个事件,关联一个观察者

    public void addEvent(Object object, String methodName, java.lang.Object[] args){
        events.add(new Event(object, methodName, args));
    }

    //发布事件
    public void subscribeNotice() throws Exception {
        for (Event e: events){
            e.invoke();
        }
    }
}

7.3 Source被观察者抽象接口

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 14:56
 **/
public interface Source {

    /**
     * 添加观察者
     *
     * @param object 观察者实例
     * @param methodName 执行方法名称
     * @param args 方法的参数
     */
    void addListener(Object object, String methodName, Object...args);

    /**
     * 通知
     * @throws Exception
     */
    void subscribeNotice() throws Exception;
}

7.4 Source具体实现类ConcreteSource

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 14:59
 **/
public class ConcreteSource implements Source{

    private final EventHandler eventHandler = new EventHandler();

    @Override
    public void addListener(Object object, String methodName, Object...args) {
        eventHandler.addEvent(object, methodName, args);
    }

    @Override
    public void subscribeNotice() throws Exception {
        eventHandler.subscribeNotice();
    }
}

7.5 观察者1-看电视选手TvObserver

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 15:18
 **/
public class TvObserver {

    public void stopWatchTv(String reason){
        System.out.println("赶快别看电视了, " + reason);
    }
}

7.6 观察者2-摸鱼选手AttachFishObserver

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 15:18
 **/
public class AttachFishObserver {

    public void stopAttachFish(Date date) {
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(f.format(date));
        System.out.println("赶快别看摸鱼了, 看看时间啊,我的哥");
    }
}

7.7 主函数

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-08-31 11:18
 **/
public class MainClass {
    public static void main(String[] args) throws Exception {
        ConcreteSubject concreteSubject = new ConcreteSubject();
        GameObserver gameObserver = new GameObserver();
        MovieObserver movieObserver = new MovieObserver();

        concreteSubject.addObserver(gameObserver);
        concreteSubject.addObserver(movieObserver);

        concreteSubject.notifyAll("有内鬼");

        System.out.println("---------------------分界线----------------------");

        ConcreteSource concreteSource = new ConcreteSource();

        TvObserver tvObserver = new TvObserver();
        AttachFishObserver attachFishObserver = new AttachFishObserver();

        concreteSource.addListener(tvObserver, "stopWatchTv", "你老妈回来了");
        concreteSource.addListener(attachFishObserver, "stopAttachFish", new Date());

        concreteSource.subscribeNotice();
    }
}

7.8 执行结果

8 总结

本文依据自己的理解,介绍了观察者模式,通过观察者模式的不足,引出委托事件,给出代码与执行结果。

9 引用

1.用java语言实现事件委托模式

2.观察者模式和事件委托

3.观察者设计模式 Vs 事件委托(java)

4.《大话设计模式》

10 源码

https://github.com/airhonor/design-pattern-learning

猜你喜欢

转载自blog.csdn.net/honor_zhang/article/details/120020490