23种设计模式之观察者模式

老杨叔叔 2016-02-03

       我们在做企业开发的时候,有时会遇到一种情况就是有一个类,其部分数据被多个类所依赖,被依赖的类也不知道依赖类的具体实现情况,此时我们就可以使用观察者模式,这里依赖类称为观察者,被依赖的类称为被观察者。观察者可以有多个,被观察者只能有一个,被观察者的数据变化将“同步实时“更新到观察者类中。

       如果一个类被多个类所依赖,并且这些类产生的行为将会因为这个类的部分数据的变化而变化,那么我们就可以使用观察者模式,由于java的基础类库中已经提供了对于观察者模式的支持,因而在java中使用观察者模式非常简单。观察者类只需要继承Observer类,并且实现update方法即可,而被观察者类只需要继承Observable类即可。

       在生活中我们有很多可以使用观察者模式的例子,比如一个班的老师改变了自己的课程安排,那么学生就必须根据老师的安排来做出相应的改变,即根据新课表在不同的时间段上课;再比如天气预警,各个不同的部门(政府,企业,个人等)需要根据不同的天气采取不同的措施。这里我们以老师更改课表的例子来讲解观察者模式。

       我们假设老师正常的课表时间是2016-02-03 10:00:00,但是由于学校调遣安排将老师的课表修改为2016-02-03 15:00:00。因此,学生的上课时间也将随之改变,这里我们创建了四个类:Student,Teacher,School和TeacherFactory。Student和Teacher是一个多对一的关系,并且Teacher类中有一个字段course,该字段说明该老师上课的时间。在Teacher类中对course字段的设值方法中,我们调用了继承自Observable类中的setChanged()和notifyObservers()方法,setChanged()方法用于表名该字段被更改了,在notifyObservers()方法中会检查是否调用过setChanged()方法,如果未调用过则不会对所有的观察者类进行更新,如前所述,notifyObservers()方法首先判断是否做出了改变,如果判断通过,则对储存的所有观察者调用其update()方法对其进行更新,这里在Teacher类的设置函数中调用这两个方法的原因是被观察者观察的字段course的改变只可能发生在该设置函数中(不利用反射的前提下)。在Student类中,我们必须实现Observer接口的update()方法,当被观察者的属性发生改变时,就会调用观察者的update()方法。这里需要说明的是,我们需要在Student类中的构造函数中添加一个Teacher类的实例,并且调用其addObserver()方法,该方法是将当前类的对象(Student类)添加到观察者序列中,另外我们还需要手动调用一次update()方法,调用该方法的目的是将观察者类中的属性初始化,即获取Teacher类中的初始数据。

       以下是Teacher类,即被观察者类:

import java.util.Date;
import java.util.Observable;

public class Teacher extends Observable {

  private String name;
  private String course;

  public Teacher(String name) {
    this.name = name;
  }

  public String getCourse() {
    return course;
  }

  public void setCourse(String course) {
    this.course = course;
    setChanged();
    notifyObservers();
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

        其次是Student类,即观察者类,这里goToClass()方法可以理解为Student类将要进行的操作:

import java.util.Date;
import java.util.Observable;
import java.util.Observer;

import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

public class Student implements Observer {
  private String name;
  private Teacher teacher;
  private String timeToClass;

  public Student(String name) {
    this.name = name;

    teacher = TeacherFactory.getTeacher("Smith");
    teacher.addObserver(this);
    update(teacher, null);
  }

  @Override
  public void update(Observable o, Object arg) {
    timeToClass = teacher.getCourse();
    goToClass();
  }

  public void goToClass() {
    System.out.println(name + " will goto class at " + timeToClass);
  }
}

        为了方便各个类中对Teacher类的使用,我们定义了一个Teacher类的字典工厂,可以通过该类在需要的时候或许Teacher对象:

import java.util.Dictionary;
import java.util.Hashtable;

public class TeacherFactory {

  private static Dictionary<String, Teacher> instance = new Hashtable<>();

  static {
    store(new Teacher("Smith"));
    store(new Teacher("Jack"));
    store(new Teacher("Rose"));
    store(new Teacher("Bob"));
  }

  private static void store(Teacher teacher) {
    instance.put(teacher.getName(), teacher);
  }

  public static Teacher getTeacher(String teacherName) {
    if (null != teacherName && !"".equals(teacherName)) {
      return instance.get(teacherName);
    }
    return null;
  }
}

        最后是School类,该类起到一个管理作用,学校出于一些原因将Smith老师的课程从2016年02月03日上午10点修改为下午3点,修改的同时,由于学生Tom和Rock一直在观察Smith老师的课表,因而学生也就知道上课时间更改了,并且于修改后的时间去上课:

public class School {

  public static void main(String[] args) {
    init();
    System.out.println("*****running*****");
    Teacher teacher = TeacherFactory.getTeacher("Smith");
    teacher.setCourse("2016-02-04 15:00:00");
  }

  public static void init() {
    Teacher teacher = TeacherFactory.getTeacher("Smith");
    teacher.setCourse("2016-02-04 10:00:00");
    System.out.println("*****initialize*****");
    Student tim = new Student("Tim");
    Student rock = new Student("Rock");
  }
}

        运行后的效果如下所示:

    *****initialize*****
    Tim will goto class at 2016-02-04 10:00:00

    Rock will goto class at 2016-02-04 10:00:00

    *****running*****

    Rock will goto class at 2016-02-04 15:00:00

    Tim will goto class at 2016-02-04 15:00:00

       这里初始化的时候Tim是在Rock上方,而在执行的时候Tim是在Rock下方,这是因为被观察者在对观察者进行更新的时候是按照观察者添加的顺序的倒序进行更新的。

       以上就是本次博客的全部内容,希望大家能够喜欢,另外,除夕将至,在这里预祝大家新年快乐,阖家幸福,新年新气象,谢谢大家!

相关推荐