23个java设计模式(二)-- 观察者模式

2016年03月07日 原创
关键词: java 设计模式
摘要 观察者模式是定义对象间的一种一对多(多对多也可以)的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并被自动更新。

一、概述。

观察者模式是定义对象间的一种一对多(多对多也可以)的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并被自动更新。

它是关于多个对象想知道一个对象中数据变化情况的一种成熟的模式。观察者模式中有一个称作“主题”的对象和若干个称作“观察者”的对象,“主题”和“观察者”间是一种一对多的依赖关系,当“主题”的状态发生变化时,所有“观察者”都得到通知。

二、模式的结构。

观察者模式的结构中包括四种角色。

  • 主题(Subject):主题是一个接口,该接口规定了具体主题需要实现的方法,比如:添加、删除观察者以及通知观察者更新数据的方法。
  • 观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。
  • 具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题需使用一个集合存放观察者的引用,以便数据发生变化时通知具体观察者。
  • 具体观察者(ConcreteObserver):具体观察者是实现观察者接口类的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者。

三、简单的例子程序。

下面的例子程序演示了一个大学毕业生和一个归国留学者都能及时知道“求职中心”最新的职业需求信息。

主题(Subject)

public interface Subject {
	public void addObserver(Observer o);
	public void deleteObserver(Observer o);
	public void notifyObservers();
}

观察者(Observer)

public interface Observer {
	public void hearTelephone(String heardMess);
}

具体主题

 
public class SeekJobCenter implements Subject {
	private String message;
	private boolean changed;
	private ArrayList<Observer> personList; //存放观察者引用的集合

	public SeekJobCenter() {
		super();
		personList = new ArrayList<>();
		message = "";
		changed = false;
	}

	@Override
	public void addObserver(Observer o) {
		if (!personList.contains(o))
			personList.add(o);
	}

	@Override
	public void deleteObserver(Observer o) {
		if (personList.contains(o))
			personList.remove(o);
	}

	@Override
	public void notifyObservers() {
		if (changed) {
			for (Observer o : personList) { //通知所有观察者
				o.hearTelephone(message);
			}
			changed = false;
		}
	}
	
	public void giveNewMessage(String str) {
		if(str.equals(message)) { //如果信息相同就不通知
			changed = false;
		} else {
			message = str;
			changed = true;
		}
	}

}

具体观察者

public class UniversityStudent implements Observer{
	private Subject subject;
	private File myFile;
	public UniversityStudent(Subject subject, String fileName) {
		this.subject = subject;
		subject.addObserver(this); //使当前实例成为subject所引用的具体主题的观察者
		myFile = new File(fileName);
	}
	@Override
	public void hearTelephone(String heardMess) {
		try {
			RandomAccessFile out = new RandomAccessFile(myFile, "rw");
			out.seek(out.length());
			byte[] b = heardMess.getBytes();
			out.write(b);
			System.out.println("我是一名大学生,我向文件"+myFile.getName()+"写入如下内容:");
			System.out.println(heardMess);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

public class OverseaStudent implements Observer{
	private Subject subject;
	private File myFile;
	public OverseaStudent(Subject subject, String fileName) {
		this.subject =subject;
		subject.addObserver(this);
		myFile = new File(fileName);
	}
	
	@Override
	public void hearTelephone(String heardMess) {
		try {
			boolean ok = heardMess.contains("java程序员")||heardMess.contains("软件");
			if(ok) {
				RandomAccessFile out = new RandomAccessFile(myFile, "rw");
				out.seek(out.length());
				byte[] b = heardMess.getBytes();
				out.write(b);
				System.out.println("我是一个海归,我向文件"+myFile.getName()+"写入如下内容:");
				System.out.println(heardMess);
			} else {
				System.out.println("我是海归,这次的信息中没有我需要的信息");
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

三、观察者模式中的“推”数据与“拉“数据。

具体主题在使用主题规定的方法通知具体接受者更新数据时会出现下列两种方式。

1、推数据方式。

  推数据方式是指具体主题将变化后的数据全部交给具体观察者,即将变化后的数据传递给具体观察者用于更新数据方法的参数。当具体主题认为具体观察者需要这些变换后的全部数据时往往采用推数据方式。

2、拉数据方式。

  拉数据方式是指具体主题不将变化后的数据交给具体观察者,而是提供了获得这些数据的方法,具体观察者在得到通知后,可以调用具体主题提供的方法得到数据(观察者自己把数据“拉”过来),但需要自己判断数据是否发生了变化。当具体主题不知道具体观察者是否需要这些变换后的数据时往往采用拉数据的方式。

  总的来说,推数据方式就是主题把所有数据都传递给了观察者;而推数据方式则是主题提供了获取数据的方法,需要观察者自己来调用这些方法来获取数据。

四、观察者模式的优点。

  1. 具体主题和具体观察者是松耦合关系。
  2. 观察者模式满足“开-闭原则”。

五、使用观察者模式的情景。

  1. 当一个对象的数据更新时需要通知其他对象,但这个对象又不希望和被通知的那些对象形成紧耦合。
  2. 当一个对象的数据更新时,这个对象需要让其他对象也各自更新自己的数据,但这个对象不知道具体有多少对象需要更新数据。

 六、Java API中的Observable类与Observer接口。

由于观察者模式在Java程序中使用得十分广泛,因此java.util包提供了复核观察者模式的Observable类与Observer接口。用java.util包提供的观察者模式,能够使得设计统一。但是由于Observable是类而不是接口,由于java不支持多继承,导致不能继承其他类。

 

更多的例子程序可以访问博主的github  https://github.com/cxylpy/design-patterns