Java设计模式--观察者模式
声明:本文根据慕课网GerryZhang老师的精品课程整理来的:慕课网
什么是设计模式(Design Pattern)?
设计模式是一套被反复使用,多数人知晓的,经过分类编目的,代码设计经验的总结。
观察者模式的定义?
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知,并被自动更新。
观察者模式的分类?
1.推模型:目标对象主动向观察者推送目标的详细信息,推送的信息通常是目标对象的全部或部分数据,相当于广播通信
2.拉模型:目标对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。一般这种模型的实现中,会把目标对象自身通过update方法传递给观察者。
Java中提供的观察者实现与自己实现的对比:
1.不需要再定义观察者和目标的接口了,JDK帮忙定义了
2.具体的目标实现里面不需要再维护观察者的注册信息了,这个在Java中的Observable类里面已经帮忙实现了。
3.触发通知的方式有一些变化,要先调用setChanged方法,这个是Java为了帮助实现更精确的触发控制而提供的功能。
4.具体观察者的实现里面,update方法其实能同时支持推模型和拉模型,这个是Java在定义的时候,就已经考虑进去了。
观察者模式的优点和缺点:
优点:
1.观察者模式实现了观察者和目标之间的抽象耦合。
2.观察者模式实现了动态联动。
3.观察者模式支持广播通信。
缺点:
可能会引起无谓的操作
在什么情况下使用观察者模式:
1.当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化。
2.如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变。
3.当一个对象必须通知其他的对象,但你又希望这个对象和其他被通知的对象是松散耦合的。
推模型示例:
1.创建观察者接口和目标对象
/**
* 这是一个观察者接口,定义一个更新的接口给那些在目标发生改变时被通知的对象
* @author Administrator
*
*/
public interface Observer {
/**
* 更新的接口
* @Description: TODO
* @param subject
* @returnType: void
*/
public void update(WeatherSubject subject);
}
/** * 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口。 * @author Administrator * */ public class WeatherSubject { // 用来保存注册的观察者对象 List<Observer> observers = new ArrayList<Observer>(); /** * 把订阅的人添加到订阅者列表中 */ public void attach(Observer observer) { observers.add(observer); } /** * 从集合中删除观察者 */ public void detach(Observer observer) { if (observers.contains(observer)) { observers.remove(observer); } } /** * 通知所有注册的观察者对象 */ protected void notifyObservers() { for (Observer observer : observers) { observer.update(this); } } }
2.分别创建具体的目标对象和具体的观察者对象
/**
* 具体的目标对象,负责把有关状态存入到相应的观察者对象中
* @author Administrator
*
*/
public class ConcreteWeatherSubject extends WeatherSubject {
//获取天气的内容信息
private String weatherContent;
/**
* @return the weatherContent
*/
public String getWeatherContent() {
return weatherContent;
}
/**
* @param weatherContent the weatherContent to set
*/
public void setWeatherContent(String weatherContent) {
this.weatherContent = weatherContent;
//内容有了,说明天气更细了,通知所有的订阅人
this.notifyObservers();
}
}
/**
* 具体的观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
* @author Administrator
*
*/
public class ConcreteObserver implements Observer {
//观察者的名字,是谁收到了这个信息
private String observerName;
//天气内容的情况,这个消息从目标处获取
private String weatherContent;
//提醒的内容,根据不同的人提醒不同的信息
private String remindThing;
/**
* 获取目标类的状态同步到观察者的状态中
*/
@Override
public void update(WeatherSubject subject) {
weatherContent = ((ConcreteWeatherSubject)subject).getWeatherContent();
System.out.println(observerName+"收到了"+weatherContent+","+remindThing);
}
/**
* @return the observerName
*/
public String getObserverName() {
return observerName;
}
/**
* @param observerName the observerName to set
*/
public void setObserverName(String observerName) {
this.observerName = observerName;
}
/**
* @return the weatherContent
*/
public String getWeatherContent() {
return weatherContent;
}
/**
* @param weatherContent the weatherContent to set
*/
public void setWeatherContent(String weatherContent) {
this.weatherContent = weatherContent;
}
/**
* @return the remindThing
*/
public String getRemindThing() {
return remindThing;
}
/**
* @param remindThing the remindThing to set
*/
public void setRemindThing(String remindThing) {
this.remindThing = remindThing;
}
}
3.创建测试类进行测试
程序运行逻辑(准备阶段):
1.创建目标对象
2.创建观察者对象
3.向目标对象注册观察者对象
程序性运行逻辑(运行阶段):
1.改变目标对象的状态
2.目标对象通知所有观察者对象运行相应的处理
3.观察者对象回调目标对象,获取相应的数据
public class Test {
public static void main(String[] args) {
//1.创建一个目标
ConcreteWeatherSubject subject = new ConcreteWeatherSubject();
//2.创建一个观察者
ConcreteObserver girlObserver = new ConcreteObserver();
girlObserver.setObserverName("黄明的女朋友");
girlObserver.setRemindThing("天气很好,适合约会");
ConcreteObserver mumObserver = new ConcreteObserver();
mumObserver.setObserverName("黄明的老妈");
mumObserver.setRemindThing("天气很好,适合购物");
//3.注册观察者
subject.attach(girlObserver);;
subject.attach(mumObserver);
//4.目标发布天气
subject.setWeatherContent("明天天气晴朗,气温28度");
}
}
拉模型示例:
1.创建观察者接口和目标对象
/**
* 这是一个观察者接口,定义一个更新的接口给那些在目标发生改变时被通知的对象
* @author Administrator
*
*/
public interface Observer {
/**
* 更新的接口
* @Description: TODO
* @param subject
* @returnType: void
*/
public void update(String content);
}
/** * 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口。 * @author Administrator * */ public class WeatherSubject { // 用来保存注册的观察者对象 List<Observer> observers = new ArrayList<Observer>(); /** * 把订阅的人添加到订阅者列表中 */ public void attach(Observer observer) { observers.add(observer); } /** * 从集合中删除观察者 */ public void detach(Observer observer) { if (observers.contains(observer)) { observers.remove(observer); } } /** * 通知所有注册的观察者对象 */ protected void notifyObservers(String content) { for (Observer observer : observers) { observer.update(content); } } }
2.分别创建具体的目标对象和具体的观察者对象
/**
* 具体的目标对象,负责把有关状态存入到相应的观察者对象中
* @author Administrator
*
*/
public class ConcreteWeatherSubject extends WeatherSubject {
//获取天气的内容信息
private String weatherContent;
/**
* @return the weatherContent
*/
public String getWeatherContent() {
return weatherContent;
}
/**
* @param weatherContent the weatherContent to set
*/
public void setWeatherContent(String weatherContent) {
this.weatherContent = weatherContent;
//内容有了,说明天气更细了,通知所有的订阅人
this.notifyObservers(weatherContent);
}
}
/**
* 具体的观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
* @author Administrator
*
*/
public class ConcreteObserver implements Observer {
//观察者的名字,是谁收到了这个信息
private String observerName;
//天气内容的情况,这个消息从目标处获取
private String weatherContent;
//提醒的内容,根据不同的人提醒不同的信息
private String remindThing;
/**
* 获取目标类的状态同步到观察者的状态中
*/
@Override
public void update(String content) {
weatherContent = content;
System.out.println(observerName+"收到了"+weatherContent+","+remindThing);
}
/**
* @return the observerName
*/
public String getObserverName() {
return observerName;
}
/**
* @param observerName the observerName to set
*/
public void setObserverName(String observerName) {
this.observerName = observerName;
}
/**
* @return the weatherContent
*/
public String getWeatherContent() {
return weatherContent;
}
/**
* @param weatherContent the weatherContent to set
*/
public void setWeatherContent(String weatherContent) {
this.weatherContent = weatherContent;
}
/**
* @return the remindThing
*/
public String getRemindThing() {
return remindThing;
}
/**
* @param remindThing the remindThing to set
*/
public void setRemindThing(String remindThing) {
this.remindThing = remindThing;
}
}
3.创建测试类进行测试
public class Test {
public static void main(String[] args) {
// 1.创建一个目标
ConcreteWeatherSubject subject = new ConcreteWeatherSubject();
// 2.创建一个观察者
ConcreteObserver girlObserver = new ConcreteObserver();
girlObserver.setObserverName("黄明的女朋友");
girlObserver.setRemindThing("天气很好,适合约会");
ConcreteObserver mumObserver = new ConcreteObserver();
mumObserver.setObserverName("黄明的老妈");
mumObserver.setRemindThing("天气很好,适合购物");
// 3.注册观察者
subject.attach(girlObserver);
;
subject.attach(mumObserver);
// 4.目标发布天气
subject.setWeatherContent("明天天气晴朗,气温28度");
}
}
使用Java提供的观察者实现进行实例测试:
1.创建目标的具体实现类,继承于Java提供的Observable类
/** * 天气目标的具体实现类 * @author Administrator * */ public class ConcreteWeatherSubject extends Observable { //天气情况的内容 private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; //天气情况有了,就要通知所有的观察者 //注意在通知之前,在用Java中的Observer模式的时候,下面这句话不可少 this.setChanged(); //然后主动通知,这里用推的模式实现 this.notifyObservers(content); //如果是拉的模式实现,使用不带参数的 //this.notifyObservers(); } }
2.创建具体的观察者对象,实现Java提供的Observer接口
/**
* 具体的观察者对象
* @author Administrator
*
*/
public class ConcreteObserver implements Observer {
//观察者名称的变量
private String observerName;
@Override
public void update(Observable o, Object arg) {
//第一种是推的方式
System.out.println(observerName+"收到了消息,目标推送过来的是:"+arg);
//第二种是拉的方式
System.out.println(observerName+"收到了消息,主动到目标对象中去拉,拉的内容是"
+((ConcreteWeatherSubject)o).getContent());
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
}
3.测试类
/**
* 测试类
* @author Administrator
*
*/
public class Test {
public static void main(String[] args) {
//创建天气作为一个目标,也可以说是被观察者
ConcreteWeatherSubject subject = new ConcreteWeatherSubject();
//创建黄明的女朋友作为观察者
ConcreteObserver girlObserver = new ConcreteObserver();
girlObserver.setObserverName("黄明的女朋友");
//创建黄明的老妈作为观察者
ConcreteObserver mumObserver = new ConcreteObserver();
mumObserver.setObserverName("黄明的老妈");
//注册观察者
subject.addObserver(mumObserver);
subject.addObserver(girlObserver);
//目标更新天气情况
subject.setContent("天气晴,气温28度");
}
}