Java观察者模式&事件委托(通过dota和王者荣耀故事讲解)

版权声明:作者: 阿顾, 转载请写明出处, 谢谢 https://blog.csdn.net/u010452388/article/details/82962592

故事情景

 首先介绍下故事的三位同学,阿顾、蛋蛋和洋洋,蛋蛋,洋洋喜欢玩游戏,但是又生怕班主任回来了被抓到,所以他们就找到了阿顾同学帮他们把风,但是阿顾同学不是谁都通知的,只有到阿顾那里登记的,阿顾同学才会通知这些登记的人,下面我们来模拟下整个流程:

蛋蛋说:“阿顾,我在玩dota,班主任回来了,通知一声”

阿顾说:“好的,我记录一下”

洋洋说:“阿顾,我在玩王者荣耀,班主任回来了,通知一声”

阿顾说:“好的,我记录一下”

阿顾此时正在把风,过了一会,发现班主任回来了,立马通知刚刚在这里登记的蛋蛋和洋洋同学

蛋蛋立即关闭dota,继续学习;洋洋立即关闭王者荣耀,继续学习(流程结束

到这里,我们看一下代码

目录

故事情景

一 观察者模式

1.1 代码结构

1.2 主题

1.3 观察者(抽象类)

1.4 阿顾同学

1.5 蛋蛋同学

1.6 洋洋同学

1.7 测试

1.8 运行结果

1.9 观察者模式的优缺点

1.9.1 优点

1.9.2 缺点

1.9.3 解决缺点

二 事件委托

2.1 代码结构

2.2 事件类

2.3 事件处理类

2.4 通知者(抽象类)

2.5 阿顾同学

2.6 蛋蛋同学

2.7 洋洋同学

2.8 测试

2.9 运行结果

三 参考文献

四 源码地址


一 观察者模式

1.1 代码结构

1.2 主题

主题是一个接口,提供了观察者抽象类添加方法,移除方法,以及通知功能

public interface Subject {

    //增加观察者
    public void add(Observer observer);

    //移除观察者
    public void remove(Observer observer);

    //通知观察者
    public void notifyObserver();
    
}

1.3 观察者(抽象类)

/**
 * 观察者(抽象类)
 */
public abstract class Observer {
    //抽象方法(停止做某事)
    public abstract void stopDoingSomeThing();
}

1.4 阿顾同学

这里实现了主题,并且添加了一个观察者抽象类的集合,所以登记的同学都放到这个集合里,如果发现班主任回来了,直接直接执行通知方法(遍历集合里的同学


public class AguSubject implements Subject {
    //主题状态
    private String subjectState;

    public String getSubjectState() {
        return subjectState;
    }

    public void setSubjectState(String subjectState) {
        this.subjectState = subjectState;
    }

    //观察者列表(所有需要通知的对象都会放到这个集合里)
    private List<Observer> observerList =new ArrayList<Observer>();
    //添加观察者
    public void add(Observer observer) {
        observerList.add(observer);
    }
    //移除观察者
    public void remove(Observer observer) {
        observerList.remove(observer);
    }
    //通知观察者
    public void notifyObserver() {
        for (Observer observer : observerList) {
            //父类执行这个方法,执行的是子类重写的方法
            observer.stopDoingSomeThing();
        }
    }
}

1.5 蛋蛋同学

public class DanDanObserver extends Observer {
    //姓名
    private String name;
    //阿顾同学
    private AguSubject subject;
    //有参构造函数
    public DanDanObserver(String name, AguSubject subject) {
        this.name = name;
        this.subject = subject;
    }
    //重写父类方法
    public void stopDoingSomeThing() {
        System.out.println(subject.getSubjectState() + this.name + "关闭了<dota2>,继续学习");
    }
}

1.6 洋洋同学

public class YangYangObserver extends Observer {
    //姓名
    private String name;
    //阿顾同学
    private AguSubject subject;
    //有参构造函数
    public YangYangObserver(String name,AguSubject subject){
        this.name=name;
        this.subject=subject;
    }
    //重写父类方法
    public void stopDoingSomeThing() {
        System.out.println(subject.getSubjectState()+this.name+"关闭了<王者荣耀>,继续学习");
    }
}

1.7 测试

public class AppStart {

    public static void main(String[] args) {
        //创建阿顾同学(负责把风)
        AguSubject aguSubject = new AguSubject();
        //创建蛋蛋
        DanDanObserver danObserver = new DanDanObserver("蛋蛋", aguSubject);
        //创建洋洋
        YangYangObserver yangObserver = new YangYangObserver("洋洋", aguSubject);
        //蛋蛋在阿顾同学那登记,如果老师来了通知下
        aguSubject.add(danObserver);
        //洋洋在阿顾同学那登记,如果老师来了通知下
        aguSubject.add(yangObserver);
        
        //阿顾发现老师回来了
        aguSubject.setSubjectState("老师回来了----");
        //通知所有登记过的同学
        aguSubject.notifyObserver();
    }
}

1.8 运行结果

1.9 观察者模式的优缺点

1.9.1 优点

1. 观察者和主题进行了不错的解耦

1.9.2 缺点

1.主题还是要依赖抽象观察者(Subject里的方法要传入Observer),如果没有抽象观察者呢

2.每个具体的观察者方法都一样(蛋蛋和洋洋里都要重写stopDoingSomething()方法,并且他们的方法名和参数是一样的)

1.9.3 解决缺点

通过事件委托可以解决


二 事件委托

2.1 代码结构

2.2 事件类

这里是把一个对象,对象的方法,方法的参数 封装到事件类中,这样拿到事件,就拿到了对象的方法,就可以直接执行对应的方法了

/**
 * 事件类,就是将一个对象,对象的方法,方法中的参数 作为事件类的属性
 */
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;
        generateParamTypes(args);
    }
    //获取执行方法的参数的类型
    public void generateParamTypes(Object[] args) {
        this.paramTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            paramTypes[i] = args[i].getClass();
        }
    }
    //方法的执行
    public void invoke() throws Exception {
        //通过反射以及方法名和参数类型获取到对应的方法
        Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
        if(null==method){
            return;
        }
        //方法执行
        method.invoke(this.getObject(),this.getParams());
    }

    //get和set方法省略...
}

2.3 事件处理类

这里又将Event的集合封装到EventHandler中

public class EventHandler {
    //事件列表(就是传进来某个对象的方法的一个集合)
    private List<Event> eventList;

    public EventHandler() {
        eventList = new ArrayList<Event>();
    }
    //添加某个对象要执行的方法,及参数
    public void addEvent(Object object, String methodName, Object... args) {
        Event event = new Event(object, methodName, args);
        eventList.add(event);
    }
    //通知所有观察者
    public void notifyObserver() throws Exception {
        for (Event e : eventList) {
            e.invoke();
        }
    }
}

2.4 通知者(抽象类)

这里又将EventHandler封装到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 notifyObserver() throws Exception;
    
}

2.5 阿顾同学

public class AguNotifier extends Notifier {

    //添加
    public void addListener(Object object, String methodName, Object... args) {
        System.out.println("有新的同学委托阿顾");
        this.getEventHandler().addEvent(object, methodName, args);
    }

    //通知
    public void notifyObserver() throws Exception {
        System.out.println("======阿顾同学正在观察...班主任来了没======");
        Thread.sleep(2000);
        System.out.println("过了2秒后,阿顾通知:班主任人来了");

        this.getEventHandler().notifyObserver();
    }
}

2.6 蛋蛋同学

public class DanDanObserver {

    public DanDanObserver(){
        System.out.println("蛋蛋 正在打dota2,时间:"+new Date());
    }

    public void stopPlayDota2(String book){

        System.out.println("蛋蛋 关闭了dota2,继续学习"+book+",时间:"+new Date());
    }
}

2.7 洋洋同学

public class YangYangObserver {

    public YangYangObserver() {

        System.out.println("洋洋 正在打王者荣耀,时间:"+new Date());
    }

    public void stopKingGlory(Double javaVersion) {
        System.out.println("洋洋 关闭了王者荣耀,继续研究java"+javaVersion+"新特性,时间:" + new Date());
    }
}

2.8 测试

public class AppStart {
    public static void main(String[] args) {
        //专门把风的阿顾同学
        AguNotifier aguNotifier = new AguNotifier();
        //玩dota2的蛋蛋同学
        DanDanObserver danDanObserver = new DanDanObserver();
        //玩王者的洋洋同学
        YangYangObserver yangYangObserver = new YangYangObserver();
        //玩dota2的蛋蛋让阿顾帮忙看一下,如果班主任来了通知下
        aguNotifier.addListener(danDanObserver,"stopPlayDota2","<java编程思想>");
        //玩王者的洋洋让阿顾帮忙看一下,如果班主任来了通知下
        aguNotifier.addListener(yangYangObserver,"stopKingGlory",1.8);

        try {
            //阿顾开始通知
            aguNotifier.notifyObserver();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

2.9 运行结果

三 参考文献

1.大话设计模式

2.https://blog.csdn.net/gdutxiaoxu/article/details/51824769

四 源码地址

https://github.com/guguoyu/event-handler

猜你喜欢

转载自blog.csdn.net/u010452388/article/details/82962592