观察者模式 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!");
         }
     }
 }

Java设计模式系列之观察者模式的更多相关文章

  1. java设计模式系列之设计模式概要(1)

    一.什么是设计模式 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. ...

  2. Java设计模式10:观察者模式

    观察者模式 观察者模式也叫作发布-订阅模式,也就是事件监听机制.观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态上发生变化时,会通知所有观察者对象,使他 ...

  3. Java设计模式系列之工厂模式

    工厂模式将大量有共同接口的类实例化,工厂模式可以实现动态决定实例化哪一个类的对象,工厂模式在<Java与模式>中分为三类:1)简单工厂模式(Simple Factory):添加某一种类型的 ...

  4. Java设计模式系列之动态代理模式(转载)

    代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public interface Sub ...

  5. Java设计模式系列之适配器模式

    适配器模式的定义 将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.(就类似于我们充电器的转接头将220V的电压转换成我们的手机端 ...

  6. Java设计模式系列之状态模式

    状态模式(State)的定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它 ...

  7. Java设计模式之《观察者模式》及应用场景

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6513651.html 观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监 ...

  8. C#设计模式系列:观察者模式(Observer)

    在软件构建过程中,需要为某些对象建立一种“通知依赖关系”,即一个对象的状态发生改变,所有的依赖对象都需要得到通知. 1.观察者模式简介 1.1>.定义 定义对象间的一种一对多的依赖关系,当一个对 ...

  9. Java设计模式系列1--原型模式(Prototype Method)

    2014-02-14 11:27:33 声明:本文不仅是本人自己的成果,有些东西取自网上各位大神的思想,虽不能一一列出,但在此一并感谢! 原型模式,从名字即可看出,该模式的思想就是将一个对象作为原型, ...

随机推荐

  1. 启动项目的时候报驱动错误: not support oracle driver 1.0

    问题:今天在使用pom导入oracle14的包时候发现怎么都下载不过来.网上查了一下发现是因为Oracle驱动需要官方授权,所以在pom.xml文件直接配置,无法下载成功. 解决方法就是下载oracl ...

  2. 系统安全:Nessus Home版安装使用

    1.安装  下载地址:http://www.tenable.com/products/nessus/select-your-operating-system#tos 安装命令:rpm -ivh  Ne ...

  3. Linux解压和打包jar

    linux 中解压jarunzip XXX.jar -d app 打jar 进入到解压目录里面(app)jar cvfm0 MR-XDR-JMR-NEW.jar META-INF/MANIFEST.M ...

  4. java servlet手机app访问接口(二)短信验证

    今天找了几个短信平台,其实最想使用的一个是sharesdk,使用它上面http api短信功能,不仅价格低,而且最少可以充值100RMB,但是审核过于严格,对应APP还必须集成他们的短信功能,而且要上 ...

  5. redis主从 以及认证配置

    以前用redis用的很多,各种数据类型用的飞起,算是用得很溜了.不过那都是封装好的方法,自己直接调用.以前的公司比较规范,开发只是开发,很少去做跟运维相关的事情. 换了一份工作,不过这边项目刚开始起步 ...

  6. iOS 关于iphone6 和 iphone6 plus 的适配

    http://www.ui.cn/detail/26980.html 根据上面说的,iphone6 plus的屏幕的编程时的宽度应该是414,我理解的也是这样,但是我用iphone6 plus 模拟器 ...

  7. JAVA安装,环境变量配置

    JAVA环境变量设置 PATH %JAVA_HOME%\bin JAVA_HOME D:\ProgramFiles\Java\jdk1.6.0_10 CLASSPATH .;%JAVA_HOME%\l ...

  8. Linux之重定向

    Linux重定向是指修改原来默认的一些东西,对原来系统命令的默认执行方式进行改变,比如说简单的我不想看到在显示器的输出而是希望输出到某一文件中就可以通过Linux重定向来进行这项工作. Linux默认 ...

  9. Sqli-labs less 35

    Less-35 35关和33关是大致的一样的,唯一的区别在于sql语句的不同. $sql="SELECT * FROM users WHERE id=$id LIMIT 0,1"; ...

  10. eclipse环境下tomcat远程调试方法

    前提:Windows环境tomcat是以catalina.bat方式而非Windows服务形式启动(两者所使用的JAVA配置及JVM参数会有差异). 服务器段设置 方法1:修改CATALINA_OPT ...