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.创建观察者接口和目标对象
1 /** 2 * 这是一个观察者接口,定义一个更新的接口给那些在目标发生改变时被通知的对象 3 * @author Administrator 4 * 5 */ 6 public interface Observer { 7 /** 8 * 更新的接口 9 * @Description: TODO 10 * @param subject 11 * @returnType: void 12 */ 13 public void update(WeatherSubject subject); 14 }
1 /** 2 * 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口。 3 * @author Administrator 4 * 5 */ 6 public class WeatherSubject { 7 // 用来保存注册的观察者对象 8 List<Observer> observers = new ArrayList<Observer>(); 9 10 /** 11 * 把订阅的人添加到订阅者列表中 12 */ 13 public void attach(Observer observer) { 14 observers.add(observer); 15 } 16 17 /** 18 * 从集合中删除观察者 19 */ 20 public void detach(Observer observer) { 21 if (observers.contains(observer)) { 22 observers.remove(observer); 23 } 24 } 25 26 /** 27 * 通知所有注册的观察者对象 28 */ 29 protected void notifyObservers() { 30 for (Observer observer : observers) { 31 observer.update(this); 32 } 33 } 34 }
2.分别创建具体的目标对象和具体的观察者对象
1 /** 2 * 具体的目标对象,负责把有关状态存入到相应的观察者对象中 3 * @author Administrator 4 * 5 */ 6 public class ConcreteWeatherSubject extends WeatherSubject { 7 //获取天气的内容信息 8 private String weatherContent; 9 10 /** 11 * @return the weatherContent 12 */ 13 public String getWeatherContent() { 14 return weatherContent; 15 } 16 17 /** 18 * @param weatherContent the weatherContent to set 19 */ 20 public void setWeatherContent(String weatherContent) { 21 this.weatherContent = weatherContent; 22 //内容有了,说明天气更细了,通知所有的订阅人 23 this.notifyObservers(); 24 } 25 26 }
1 /** 2 * 具体的观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致 3 * @author Administrator 4 * 5 */ 6 public class ConcreteObserver implements Observer { 7 //观察者的名字,是谁收到了这个信息 8 private String observerName; 9 10 //天气内容的情况,这个消息从目标处获取 11 private String weatherContent; 12 13 //提醒的内容,根据不同的人提醒不同的信息 14 private String remindThing; 15 16 /** 17 * 获取目标类的状态同步到观察者的状态中 18 */ 19 @Override 20 public void update(WeatherSubject subject) { 21 weatherContent = ((ConcreteWeatherSubject)subject).getWeatherContent(); 22 System.out.println(observerName+"收到了"+weatherContent+","+remindThing); 23 } 24 25 /** 26 * @return the observerName 27 */ 28 public String getObserverName() { 29 return observerName; 30 } 31 32 /** 33 * @param observerName the observerName to set 34 */ 35 public void setObserverName(String observerName) { 36 this.observerName = observerName; 37 } 38 39 /** 40 * @return the weatherContent 41 */ 42 public String getWeatherContent() { 43 return weatherContent; 44 } 45 46 /** 47 * @param weatherContent the weatherContent to set 48 */ 49 public void setWeatherContent(String weatherContent) { 50 this.weatherContent = weatherContent; 51 } 52 53 /** 54 * @return the remindThing 55 */ 56 public String getRemindThing() { 57 return remindThing; 58 } 59 60 /** 61 * @param remindThing the remindThing to set 62 */ 63 public void setRemindThing(String remindThing) { 64 this.remindThing = remindThing; 65 } 66 67 }
3.创建测试类进行测试
程序运行逻辑(准备阶段):
1.创建目标对象
2.创建观察者对象
3.向目标对象注册观察者对象
程序性运行逻辑(运行阶段):
1.改变目标对象的状态
2.目标对象通知所有观察者对象运行相应的处理
3.观察者对象回调目标对象,获取相应的数据
1 public class Test { 2 3 public static void main(String[] args) { 4 //1.创建一个目标 5 ConcreteWeatherSubject subject = new ConcreteWeatherSubject(); 6 7 //2.创建一个观察者 8 ConcreteObserver girlObserver = new ConcreteObserver(); 9 girlObserver.setObserverName("黄明的女朋友"); 10 girlObserver.setRemindThing("天气很好,适合约会"); 11 12 ConcreteObserver mumObserver = new ConcreteObserver(); 13 mumObserver.setObserverName("黄明的老妈"); 14 mumObserver.setRemindThing("天气很好,适合购物"); 15 16 //3.注册观察者 17 subject.attach(girlObserver);; 18 subject.attach(mumObserver); 19 20 //4.目标发布天气 21 subject.setWeatherContent("明天天气晴朗,气温28度"); 22 } 23 24 }
拉模型示例:
1.创建观察者接口和目标对象
1 /** 2 * 这是一个观察者接口,定义一个更新的接口给那些在目标发生改变时被通知的对象 3 * @author Administrator 4 * 5 */ 6 public interface Observer { 7 /** 8 * 更新的接口 9 * @Description: TODO 10 * @param subject 11 * @returnType: void 12 */ 13 public void update(String content); 14 }
1 /** 2 * 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口。 3 * @author Administrator 4 * 5 */ 6 public class WeatherSubject { 7 // 用来保存注册的观察者对象 8 List<Observer> observers = new ArrayList<Observer>(); 9 10 /** 11 * 把订阅的人添加到订阅者列表中 12 */ 13 public void attach(Observer observer) { 14 observers.add(observer); 15 } 16 17 /** 18 * 从集合中删除观察者 19 */ 20 public void detach(Observer observer) { 21 if (observers.contains(observer)) { 22 observers.remove(observer); 23 } 24 } 25 26 /** 27 * 通知所有注册的观察者对象 28 */ 29 protected void notifyObservers(String content) { 30 for (Observer observer : observers) { 31 observer.update(content); 32 } 33 } 34 }
2.分别创建具体的目标对象和具体的观察者对象
1 /** 2 * 具体的目标对象,负责把有关状态存入到相应的观察者对象中 3 * @author Administrator 4 * 5 */ 6 public class ConcreteWeatherSubject extends WeatherSubject { 7 //获取天气的内容信息 8 private String weatherContent; 9 10 /** 11 * @return the weatherContent 12 */ 13 public String getWeatherContent() { 14 return weatherContent; 15 } 16 17 /** 18 * @param weatherContent the weatherContent to set 19 */ 20 public void setWeatherContent(String weatherContent) { 21 this.weatherContent = weatherContent; 22 //内容有了,说明天气更细了,通知所有的订阅人 23 this.notifyObservers(weatherContent); 24 } 25 26 }
1 /** 2 * 具体的观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致 3 * @author Administrator 4 * 5 */ 6 public class ConcreteObserver implements Observer { 7 //观察者的名字,是谁收到了这个信息 8 private String observerName; 9 10 //天气内容的情况,这个消息从目标处获取 11 private String weatherContent; 12 13 //提醒的内容,根据不同的人提醒不同的信息 14 private String remindThing; 15 16 /** 17 * 获取目标类的状态同步到观察者的状态中 18 */ 19 @Override 20 public void update(String content) { 21 weatherContent = content; 22 System.out.println(observerName+"收到了"+weatherContent+","+remindThing); 23 } 24 25 /** 26 * @return the observerName 27 */ 28 public String getObserverName() { 29 return observerName; 30 } 31 32 /** 33 * @param observerName the observerName to set 34 */ 35 public void setObserverName(String observerName) { 36 this.observerName = observerName; 37 } 38 39 /** 40 * @return the weatherContent 41 */ 42 public String getWeatherContent() { 43 return weatherContent; 44 } 45 46 /** 47 * @param weatherContent the weatherContent to set 48 */ 49 public void setWeatherContent(String weatherContent) { 50 this.weatherContent = weatherContent; 51 } 52 53 /** 54 * @return the remindThing 55 */ 56 public String getRemindThing() { 57 return remindThing; 58 } 59 60 /** 61 * @param remindThing the remindThing to set 62 */ 63 public void setRemindThing(String remindThing) { 64 this.remindThing = remindThing; 65 } 66 67 }
3.创建测试类进行测试
1 public class Test { 2 public static void main(String[] args) { 3 // 1.创建一个目标 4 ConcreteWeatherSubject subject = new ConcreteWeatherSubject(); 5 6 // 2.创建一个观察者 7 ConcreteObserver girlObserver = new ConcreteObserver(); 8 girlObserver.setObserverName("黄明的女朋友"); 9 girlObserver.setRemindThing("天气很好,适合约会"); 10 11 ConcreteObserver mumObserver = new ConcreteObserver(); 12 mumObserver.setObserverName("黄明的老妈"); 13 mumObserver.setRemindThing("天气很好,适合购物"); 14 15 // 3.注册观察者 16 subject.attach(girlObserver); 17 ; 18 subject.attach(mumObserver); 19 20 // 4.目标发布天气 21 subject.setWeatherContent("明天天气晴朗,气温28度"); 22 } 23 }
使用Java提供的观察者实现进行实例测试:
1.创建目标的具体实现类,继承于Java提供的Observable类
1 /** 2 * 天气目标的具体实现类 3 * @author Administrator 4 * 5 */ 6 public class ConcreteWeatherSubject extends Observable { 7 //天气情况的内容 8 private String content; 9 10 public String getContent() { 11 return content; 12 } 13 14 public void setContent(String content) { 15 this.content = content; 16 //天气情况有了,就要通知所有的观察者 17 //注意在通知之前,在用Java中的Observer模式的时候,下面这句话不可少 18 this.setChanged(); 19 //然后主动通知,这里用推的模式实现 20 this.notifyObservers(content); 21 //如果是拉的模式实现,使用不带参数的 22 //this.notifyObservers(); 23 } 24 25 }
2.创建具体的观察者对象,实现Java提供的Observer接口
1 /** 2 * 具体的观察者对象 3 * @author Administrator 4 * 5 */ 6 public class ConcreteObserver implements Observer { 7 8 //观察者名称的变量 9 private String observerName; 10 @Override 11 public void update(Observable o, Object arg) { 12 //第一种是推的方式 13 System.out.println(observerName+"收到了消息,目标推送过来的是:"+arg); 14 //第二种是拉的方式 15 System.out.println(observerName+"收到了消息,主动到目标对象中去拉,拉的内容是" 16 +((ConcreteWeatherSubject)o).getContent()); 17 } 18 public String getObserverName() { 19 return observerName; 20 } 21 public void setObserverName(String observerName) { 22 this.observerName = observerName; 23 } 24 25 }
3.测试类
1 /** 2 * 测试类 3 * @author Administrator 4 * 5 */ 6 public class Test { 7 public static void main(String[] args) { 8 //创建天气作为一个目标,也可以说是被观察者 9 ConcreteWeatherSubject subject = new ConcreteWeatherSubject(); 10 //创建黄明的女朋友作为观察者 11 ConcreteObserver girlObserver = new ConcreteObserver(); 12 girlObserver.setObserverName("黄明的女朋友"); 13 //创建黄明的老妈作为观察者 14 ConcreteObserver mumObserver = new ConcreteObserver(); 15 mumObserver.setObserverName("黄明的老妈"); 16 17 //注册观察者 18 subject.addObserver(mumObserver); 19 subject.addObserver(girlObserver); 20 21 //目标更新天气情况 22 subject.setContent("天气晴,气温28度"); 23 } 24 }