zoukankan      html  css  js  c++  java
  • 高手眼中的观察者模式有什么不一样

    @

    观察者模式,是使用很多的一种模式,初次了解,只是根据入门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 + "老师,你好!
    " +
                    "您收到了一个来自“" + gper.getName() + "”的提问,希望您解答,问题内容如下:
    " +
                    question.getContent() + "
    " +
                    "提问者:" + 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{" + "
    " +
                    "	source=" + source.getClass() + ",
    " +
                    "	target=" + target.getClass() + ",
    " +
                    "	callback=" + callback + ",
    " +
                    "	trigger='" + trigger + "',
    " +
                    "	time=" + time + "'
    " +
                    '}';
        }
    }
    
    

    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("===========触发鼠标单击事件==========" + "
    " + e);
        }
    
        public void onDoubleClick(Event e){
            System.out.println("===========触发鼠标双击事件==========" + "
    " + e);
        }
    
        public void onUp(Event e){
            System.out.println("===========触发鼠标弹起事件==========" + "
    " + e);
        }
    
        public void onDown(Event e){
            System.out.println("===========触发鼠标按下事件==========" + "
    " + e);
        }
    
        public void onMove(Event e){
            System.out.println("===========触发鼠标移动事件==========" + "
    " + e);
        }
    
        public void onWheel(Event e){
            System.out.println("===========触发鼠标滚动事件==========" + "
    " + e);
        }
    
        public void onOver(Event e){
            System.out.println("===========触发鼠标悬停事件==========" + "
    " + e);
        }
    
        public void onBlur(Event e){
            System.out.println("===========触发鼠标失焦事件==========" + "
    " + e);
        }
    
        public void onFocus(Event e){
            System.out.println("===========触发鼠标获焦事件==========" + "
    " + 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()+"
     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每一天
    不定时推送相关文章,期待和大家一起成长!!
    在这里插入图片描述


    谢谢大家支持!

  • 相关阅读:
    第三周学习进度条
    绘制echarts折线图
    第二周学习进度条
    返回一个整数数组中最大子数组的和
    软件工程第二周开课博客
    第一周学习进度条
    学习进度
    学习进度
    学习进度
    HDU 4906 (dp胡乱搞)
  • 原文地址:https://www.cnblogs.com/hufanglei/p/13052992.html
Copyright © 2011-2022 走看看