Java设计模式——观察者模式解读

观察者模式(别名:依赖,发布-订阅)

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


1.1概述(别觉得厌烦,请细看)

在许多设计中,经常设计多个对象对一个对象中的数据变化感兴趣,而且着多个对象都希望跟踪那个特殊对象中的数据变化。例如,某些寻找工作的人对“求职中心”的职业需求信息变化非常的关心,很想跟踪“求职中心”职业需求信息的变化。一位想知道“求职中心”职业需求信息的变化的人需要成为求职中心的“求职者”,即让求职中心把自己等级到求职中心的“求职者”列表中,当一个人成为求职中心的求职者后,求职中心就会及时通知他最新的职业需求信息。如果一个求职者不想知道求职中心的职业需求信息,它会要求职中心把自己从求职列表中删除,求职中心就不会通知他职业需求信息啦。

观察者模式是关于多个对象想知道一个对象中的数据变化情况的一种成熟的模式。观察者模式中有一个称作为“主题”的对象和若干个称作“观察者”的对象,“主题”和“观察者”间是一种一对多的依赖关系,当“主题”的状态发生改变时,所有“观察者”都得到通知。前面叙述的“求职中心”相当于一个具体的“主题”;每个“求职者”相当于观察者模式中的一个具体的“观察者”。


1.2 模式中的结构与使用

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

主题(Subject):主题是一个接口,该接口规定了具体主题需要实现的方法,比如,添加、删除观察者以及通知观察者更新数据的方法。

观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。

具体主题(ConcreteObserver):具体主题是实现主题接口类的一个实例,该实例中包含有可以常变化的数据。具体主题需要使用一个集合,比如ArrayList,存在观察者的引用,以便数据变化时通知具体观察者。

具体观察者(ConcreteObserver) :具体观察者是实现观察者接口类的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便让具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者。


2.1 观察者模式的UML类图

上面描述的“求职中心”对应下图中的ConcteteSubject角色,“求职者”是模式中的ConcreteObserver角色



2.2 结构的描述

下面通过一个简单的例子来描述观察者模式中的所涉及的各个角色,这个简单的问题是:有一个大学生和一个海归都希望能够及时知道“求职中心”最新的职业需求信息。上代码...

1.主题

Subject.java

package deppon.com;

//主题接口
public interface Subject {
	
	//主题接口规定的实现添加观察者的方法
	public void addObserver(Observer o);
	
	//主题接口规定的实现的删除观察者的方法
	public void deleteObsever(Observer o);
	
	//主题接口规定的通知观察者更新数据的方法
	public void notifyObserver();

}



2.观察者

Observer.java

package deppon.com;

//观察者接口
public interface Observer {
	
	//观察接口规定的更新数据方法:hearTelephone()相当于观察者模式类图中的update()方法
	public void hearTelephone(String heardMess);

}


3.具体主题

SeekJobCenter.java

package deppon.com;

import java.util.ArrayList;

//具体主题
public class SeekJobCenter implements Subject{

	String mess;
	boolean changed;
	ArrayList<Observer>personList ;//存放观察者引用的数组线性表
	
	
	SeekJobCenter(){
		personList = new ArrayList<Observer>();
		mess ="";
		changed = false;
	}
	
	@Override
	public void addObserver(Observer o) {
		//如果集合中没有添加观察者引用
		if(!(personList.contains(o))){
				personList.add(o);//  把观察者的引用添加到数组线性表
		}
	}

	
	@Override
	public void deleteObsever(Observer o) {
		if(personList.contains(o)){
			personList.remove(o);
		}
	}
	
	@Override
	public void notifyObserver() {
		//当数据变化(主题中心数据)
		if(changed){
			//通知所有的观察者
			for (int i = 0; i < personList.size(); i++) {
				Observer observer = personList.get(i);
				observer.hearTelephone(mess);//让观察者接听电话
			}
		}
		changed = false;
	}
	
	public void giveNewMess(String str){
		if(str.equals(mess)){
			changed = false;
		}else{
			mess = str;
			changed =true;
		}
	}
}


4.具体观察者(1)
package deppon.com;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

//具体观察者1 
public class UniversityStudent implements Observer {
	Subject subject;
	File myFile;
	
	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("我是一个大学生");
			System.out.println("我向文件"+myFile.getName()+"写入如下内容");
			System.out.println(heardMess);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
}

4.具体观察者(2)

package deppon.com;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

//具体观察者2
public class HaiGui  implements Observer{
	
	Subject subject;
	File myFile;
	
	
	HaiGui(Subject subject,String fileName){
		this.subject =subject;
		subject.addObserver(this);//使当前实例成为subject所引用的具体主题的观察者
		myFile = new File(fileName);
	}
	
	
	@Override
	public void hearTelephone(String heardMess) {
		try {
		//海归只对java程序员、软件等工作关键词感兴趣
		boolean boo = heardMess.contains("java程序员")||heardMess.contains("软件");
		if(boo){
				RandomAccessFile out = new RandomAccessFile(myFile, "rw");
				out.seek(out.length());
				byte[] b = heardMess.getBytes();
				out.write(b);
				System.out.println("我是一个海归");
				System.out.println("我向文件"+myFile.getName()+"写入如下内容");
				System.out.println(heardMess);
			}else{
				System.out.println("我是一个海归,这次的信息中没有我需要的信息");
			} 
			
		}catch (IOException e) {
			e.printStackTrace();
		}
	}

}


5.使用

前面已经使用观察者模式给出了可以使用的类,这些类就是一个小的框架,可以使用这些框架编写一个应用程序。
Application.java

package deppon.com;

//应用程序的入口
public class Application {

	/**
	 * @Title: main
	 * @Description: TODO
	 * @param args
	 * @return: void
	 */
	public static void main(String[] args) {
		
		SeekJobCenter  center = new SeekJobCenter();//具体主题center
		UniversityStudent laowang = new UniversityStudent(center, "A.txt");//具体观察者laowang
		HaiGui minmin=  new HaiGui(center, "B.txt");//具体观察者minmin
		center.giveNewMess("德邦公司需要10个java程序员");//具体主题给出新信息
		center.notifyObserver();//具体主题通知信息
		center.giveNewMess("立吉卜力工作室需要10个动画设计师");
		center.notifyObserver();
		center.giveNewMess("腾讯需要2个架构师");
		center.notifyObserver();
		center.giveNewMess("腾讯需要2个架构师");//信息不回更新
		center.notifyObserver();//观察者不回执行更新的操作
	}

}

6、分析验证

运行application,下面是打印输出的内容



从上面输出的信息可以看出

1、具体主题首先判断是否有新的消息,如果changed为true,有信息的消息。

2、具体主题通过调用notifyObserver方法,遍历存放观察者引用的集合来更新数据


7.观察者模式的优点

①具体观察者和具体主题是松耦合关系。由于主题(Subject)接口仅仅是依赖于观察者(Observer)接口,因此具体主题只需要知道它的观察者是实现观察者(Observer)接口的某个类的实例,而不需要知道具体是那个类。同样,由于观察者仅仅是依赖主题(Observer)接口,因此具体观察者只是知道它依赖的主题是实现主题(Subject)接口的某个类的实例,并不需要知道具体是哪个类。

②观察者模式满足“开-闭原则”。主题(Subject)接口仅仅是依赖于观察者(Observer)接口 ,这样就可以让创建具体主题的类也可以仅仅依赖于观察者(Observer)接口,因此如果增加新的实现观察者(Observer)接口的类,不需要修改创建具体主题的类的代码。同样,创建具体观察者的类仅仅是依赖于主题(Observer)接口,如果新增加了新的实现主题接口的类,也不必修改创建具体观察者类的代码。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值