zoukankan      html  css  js  c++  java
  • Java设计模式系列之观察者模式

    观察者模式 Observer的定义

      观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。

      这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己。

    第一部分

    这里有一个例子,是马士兵老师在讲解观察者模式的时候给出的例子,个人认为对理解观察者模式有很大的用处,自己查到的一些博文也写得很好,但是太过于一板一眼了,不便于去理解。具体的例子是这样的:一个小孩在睡觉,当小孩醒过来之后,爸爸要feed,爷爷要哄哄抱抱,小狗汪汪叫。在这里这个睡觉的小孩就是被观察的对象,后面三个对象就是观察者,小孩的状态发生改变的时候,就相当于一个事件被触发了,观察者(或者应该叫做监听者)会做出相应的动作。下面是具体的是代码实现。

    第一步:我们定义被观察对象

    class Child implements Runnable {
        //用List来存放不同的监听
        private List<WakeUpListener> wakeUpListeners = new ArrayList<WakeUpListener>();
    
        //List中添加监听的操作
        public void addWakenUpListener(WakeUpListener l) {
            wakeUpListeners.add(l);
        }
    
        //被观察者小孩的状态发生改变,则会通知观察者,使其执行各自的performAction方法
        public void wakeUp() {
            for (int i = 0; i < wakeUpListeners.size(); i++) {
                WakeUpListener l = wakeUpListeners.get(i);
                //
                l.performAction(new WakeUpEvent(System.currentTimeMillis(), "沙发上",
                        this));
            }
        }
    
        //监听线程的run()
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.wakeUp();
        }
    
    }

    第二步:给出监听接口,具体的观察者都去实现这个接口,具体的观察者复写接口的performAction方法,小孩的状态发生变化,做出响应

     interface WakeUpListener {
         public void performAction(WakeUpEvent wakeUpEvent);
     }

    第三步:定义具体的观察者

    /*
     * 具体观察者ConcreteObserver,被观察的对象child的状态发生变化这一事件会触发观察者的performAction方法,针对被观察者的变化做出反应
     */
    //具体观察者一
    class Dad implements WakeUpListener {
    
        public void performAction(WakeUpEvent wakeUpEvent) {
            System.out.println("..feed..");
        }
    
    }
    //具体观察者二
    class Dog implements WakeUpListener {
    
        public void performAction(WakeUpEvent wakeUpEvent) {
            System.out.println("..汪汪..");
        }
    
    }
    //具体观察者三
    class Grand implements WakeUpListener {
    
        public void performAction(WakeUpEvent wakeUpEvent) {
            System.out.println("..hug..");
        }
    
    }

    第四步:定义事件类Event,Event事件类,将观察者状态的改变封装成Event类,不同的状态对应不同的事件。在我们的例子中,小孩的状态发生改变,他的观察者Dad、Dog、Grand会针对此事件做出反应;

    class WakeUpEvent {
        //描述了事件的一些基本的信息:时间+地点+被观察对象
        private long time;
        private String location;
        private Child child;
    
        public WakeUpEvent(long time, String location, Child child) {
            super();
            this.time = time;
            this.location = location;
            this.child = child;
        }
    
        public long getTime() {
            return time;
        }
    
        public void setTime(long time) {
            this.time = time;
        }
    
        public String getLocation() {
            return location;
        }
    
        public void setLocation(String location) {
            this.location = location;
        }
    
        public Child getChild() {
            return child;
        }
    
        public void setChild(Child child) {
            this.child = child;
        }
    
    }

    第五步:下面的observers是我们的配置文件的文件名,尽量将这些动作的实现对客户端隐藏,用户不需要明白加载读取配合文件的操作,在做代码设计的时候要始终坚持这一原则。

    try {
                props.load(ObserveTest.class.getClassLoader().getResourceAsStream(
                        "Observers.properties"));
            } catch (IOException e) {
                e.printStackTrace();
            }

    我们在这里将读取配置文件的动作封装在类中:

    //这里将读取配置文件Observers.properties的操作封装在类中,使用静态代码块的形式,在类加载的时候将配置文件加载进内存
    //提高代码的灵活行,避免反复的执行加载配置文件的操作
    class PropertyMgr {
        // 重要的思想:缓存
        // 单例初步以及缓存:把硬盘上的内容缓存到内存上
        // 缓存的策略:访问最多的文件进行缓存
        private static Properties props = new Properties();
        // 这里使用了静态代码块,类加载的时候初始化一次
        static {
            try {
                props.load(ObserveTest.class.getClassLoader().getResourceAsStream(
                        "Observers.properties"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //定义成静态static方法,方便在类外直接访问
        public static String getProperty(String key) throws IOException {
            return props.getProperty(key);
    
        }
    }

    最后一步:测试一下我们的程序,这里我给出完整的代码,方便读者的调试验证(这里附上我们的配置文件Observers.properties),只是一个简单的键值对应关系:observers=Grand,Dog,Dad

    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Properties;
    
    import org.omg.CORBA.PRIVATE_MEMBER;
    
    //Event事件类,将观察者状态的改变封装成Event类,不同的状态对应不同的事件
    //在我们的例子中,小孩的状态发生改变,他的观察者Dad、Dog、Grand会针对此事件做出反应
    class WakeUpEvent {
        //描述了事件的一些基本的信息:时间+地点+被观察对象
        private long time;
        private String location;
        private Child child;
    
        public WakeUpEvent(long time, String location, Child child) {
            super();
            this.time = time;
            this.location = location;
            this.child = child;
        }
    
        public long getTime() {
            return time;
        }
    
        public void setTime(long time) {
            this.time = time;
        }
    
        public String getLocation() {
            return location;
        }
    
        public void setLocation(String location) {
            this.location = location;
        }
    
        public Child getChild() {
            return child;
        }
    
        public void setChild(Child child) {
            this.child = child;
        }
    
    }
    
    //观察者模式中的Subject(目标),被观察对象
    
    class Child implements Runnable {
        //同List来存放不同的监听
        private List<WakeUpListener> wakeUpListeners = new ArrayList<WakeUpListener>();
    
        //List中添加监听的操作
        public void addWakenUpListener(WakeUpListener l) {
            wakeUpListeners.add(l);
        }
    
        //被观察者小孩的状态发生改变,则会通知观察者,使其执行各自的performAction方法
        public void wakeUp() {
            for (int i = 0; i < wakeUpListeners.size(); i++) {
                WakeUpListener l = wakeUpListeners.get(i);
                //
                l.performAction(new WakeUpEvent(System.currentTimeMillis(), "沙发上",
                        this));
            }
        }
    
        //监听线程的run()
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.wakeUp();
        }
    
    }
    /*
     * 具体观察者ConcreteObserver,被观察的对象child的状态发生变化这一事件会触发观察者的performAction方法,针对被观察者的变化做出反应
     */
    //具体观察者一
    class Dad implements WakeUpListener {
    
        public void performAction(WakeUpEvent wakeUpEvent) {
            System.out.println("..feed..");
        }
    
    }
    //具体观察者二
    class Dog implements WakeUpListener {
    
        public void performAction(WakeUpEvent wakeUpEvent) {
            System.out.println("..汪汪..");
        }
    
    }
    //具体观察者三
    class Grand implements WakeUpListener {
    
        public void performAction(WakeUpEvent wakeUpEvent) {
            System.out.println("..hug..");
        }
    
    }
    //抽象的观察Observer
    interface WakeUpListener {
        public void performAction(WakeUpEvent wakeUpEvent);
    }
    
    public class ObserveTest {
    
        /**
         * @param args
         * @throws IOException
         * @throws ClassNotFoundException
         * @throws IllegalAccessException
         * @throws InstantiationException
         */
        public static void main(String[] args) throws Exception {
            //读取配置文件的操作改成了静态方法,使用的时候直接调用,下面的observers是我们的配置文件的文件名
            String observers[] = PropertyMgr.getProperty("observers").split(",");
            Child child = new Child();
            for (String s : observers) {
                child.addWakenUpListener((WakeUpListener) Class.forName(s)
                        .newInstance());
            }
            new Thread(child).start();
        }
    }
    
    //这里将读取配置文件Observers.properties的操作封装在类中,使用静态代码块的形式,在类加载的时候将配置文件加载进内存
    //提高代码的灵活行,避免反复的执行加载配置文件的操作
    class PropertyMgr {
        // 重要的思想:缓存
        // 单例初步以及缓存:把硬盘上的内容缓存到内存上
        // 缓存的策略:访问最多的文件进行缓存
        private static Properties props = new Properties();
        // 这里使用了静态代码块,类加载的时候初始化一次
        static {
            try {
                props.load(ObserveTest.class.getClassLoader().getResourceAsStream(
                        "Observers.properties"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //定义成静态static方法,方便在类外直接访问
        public static String getProperty(String key) throws IOException {
            return props.getProperty(key);
    
        }
    }

    运行结果:

    ..hug..
    ..汪汪..
    ..feed..

    第二部分

    面试的过程可能会问到什么是观察者模式,其实这个时候不要给他们说什么太过于理论性的东西,举例子最方便不过了。观察者模式在java中的运用其实挺多的,比如说AWT和Swing中的监听机制用到的就是观察者模式,下面我们就来模拟一下看看监听机制是如何运作的。【注意】,代码中用到的类和方法都是我们自己定义的,不是调用API中的类和方法。

    第一步:给出被监听对象:我们定义的一个按钮button

    //首先定义一个按钮
    class Button {
        //创建一个具体的事件对象
        ActionEvent e = new ActionEvent(System.currentTimeMillis(), this);
        //List存储不同的监听者对象
        private List<ActionListener> actionListeners = new ArrayList<ActionListener>();
    
        //button按钮被按下时所触发的动作
        public void buttonPressed() {
            for (int i = 0; i < actionListeners.size(); i++) {
                ActionListener l = actionListeners.get(i);
                //按下button,监听者会做出相应的动作
                l.actionPerformed(e);
            }
        }
    
        //add添加监听者的动作
        public void addActionListener(ActionListener l) {
            actionListeners.add(l);
        }
    }

    第二步:定义监听接口,具体的监听者去实现这个接口

     interface ActionListener {
         public void actionPerformed(ActionEvent e);
     }

    第三步:具体的监听者

     在这里我们定义了两个监听者类

    class MyActionListener implements ActionListener {
    
        public void actionPerformed(ActionEvent E) {
            System.out.println("button pressed");
        }
    }
    
    class MyActionListener2 implements ActionListener {
    
        public void actionPerformed(ActionEvent E) {
            System.out.println("button pressed2");
        }
    }

    第四步:定义监听事件Event,时间对象包括:时间的发生时间when+事件源

    class ActionEvent {
        long when;
        Object source;
    
        public ActionEvent(long when, Object source) {
            super();
            this.when = when;
        }
    
        public long getWhen() {
            return when;
        }
    
        public Object getSource() {
            return source;
        }
    }

    第五步:给出测试代码

    public class Test {
        public static void main(String args[]) {
    
            Button b = new Button();
            b.addActionListener(new MyActionListener());
            b.addActionListener(new MyActionListener2());
            b.buttonPressed();
        }
    }

    运行结果:

    button pressed
    button pressed2

    最后给出完整代码方便理解调试:

    package com.observer.awt;
    
    import java.util.ArrayList;
    
    import java.util.List;
    
    public class Test {
        public static void main(String args[]) {
    
            Button b = new Button();
            b.addActionListener(new MyActionListener());
            b.addActionListener(new MyActionListener2());
            b.buttonPressed();
        }
    }
    
    //首先定义一个按钮
    class Button {
        //创建一个具体的事件对象
        ActionEvent e = new ActionEvent(System.currentTimeMillis(), this);
        //List存储不同的监听者对象
        private List<ActionListener> actionListeners = new ArrayList<ActionListener>();
    
        //button按钮被按下时所触发的动作
        public void buttonPressed() {
            for (int i = 0; i < actionListeners.size(); i++) {
                ActionListener l = actionListeners.get(i);
                //按下button,监听者会做出相应的动作
                l.actionPerformed(e);
            }
        }
    
        //add添加监听者的动作
        public void addActionListener(ActionListener l) {
            actionListeners.add(l);
        }
    }
    
    class MyActionListener implements ActionListener {
    
        public void actionPerformed(ActionEvent E) {
            System.out.println("button pressed");
        }
    }
    
    class MyActionListener2 implements ActionListener {
    
        public void actionPerformed(ActionEvent E) {
            System.out.println("button pressed2");
        }
    }
    
    interface ActionListener {
        public void actionPerformed(ActionEvent e);
    }
    
    class ActionEvent {
        long when;
        Object source;
    
        public ActionEvent(long when, Object source) {
            super();
            this.when = when;
        }
    
        public long getWhen() {
            return when;
        }
    
        public Object getSource() {
            return source;
        }
    }

    第三部分:我们在第二步给出了我们自己模拟的按钮按下触发相应动作的过程,第三部分给出AWT中,调用API中封装的一些已经实现好的类和方法,和第二步完成的是相同的动作。

    package com.observer.awt;
    
    import java.awt.Button;
    import java.awt.Frame;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.WindowAdapter;
    
    public class TestFram extends Frame {
        public void lanch() {
            // 定义一个按钮,按钮显示的信息是"press me"
            Button b = new Button("press me");
            // 添加具体监听者
            b.addActionListener(new MyActionListener());
            b.addActionListener(new MyActionListener2());
            // add方法将我们定义的button加入到Frame框架中
            this.add(b);
            // pack(),调整窗体的大小,里面可以添加参数
            this.pack();
            // 我们定义的TestFrame框架添加窗口监听
            this.addWindowListener(new WindowAdapter() {
            });
            // 使窗体可见
            this.setVisible(true);
        }
    
        public static void main(String args[]) {
            // 调用TestFram中的lanch方法,在Frame框架中定义一个按钮
            new TestFram().lanch();
        }
    
        // 具体的监听者
        private class MyActionListener implements ActionListener {
    
            public void actionPerformed(ActionEvent e) {
                // 监听者1观察到按钮被按下,做出反应
                System.out.println("button pressed");
            }
        }
    
        private class MyActionListener2 implements ActionListener {
    
            public void actionPerformed(ActionEvent e) {
                // 监听者2观察到按钮被按下,做出反应
                System.out.println("button pressed 2!");
            }
        }
    }
  • 相关阅读:
    flex datagrid进行删除或增加操作后自动刷新
    java 一个很简单的applet
    转:35岁前必成功的12级跳(男女通用)
    flex DisplayObject UIComponent的区别
    flex flash.utils.Dictionary和Object
    java 正则表达式进行剔除字符
    Flex 元数据标签使用
    使用TableAdapter的Update方法使用注意事项
    android中捕捉menu按键的点击事件
    Repeater 嵌套repeater输出不规则列表
  • 原文地址:https://www.cnblogs.com/ysw-go/p/5430875.html
Copyright © 2011-2022 走看看