1.描述
定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖他的对象都收到通知并被自动更新。
2.优点
·具体主题和具体观察者之间是松耦合关系。由于主题(Subject)接口仅仅依赖观察者(Observer)接口,因此具体主题只是知道他的具体观察者是实现某个观察者接口的类的实例,但不需要知道具体是那个类。同样,由于观察者仅仅依赖主题接口,因此具体观察者只是知道他依赖的主题是实现主题接口的某个类的实例,但不需要知道具体是那个类。
·观察者模式满足“开闭原则”
3.用途
·当一个的数据更新时需要通知其他对象,但这个对象又不希望和需要被通知的对象形成紧耦合。
·当一个对象的数据更新时,这个对象需要其他对象也各自更新自己的数据,但这个对象不知道具体有多少对象需要更新数据。
4.模式的使用
·主题(Subject):主题是一个接口,该接口规定了具体主题需要实现的方法。(添加、删除、通知观察者更新数据)
·观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来实现更新数据的方法。
·具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例可以包含经常变化的数据。具体主题需要使用一个集合,存放观察者的引用,一遍数据变化时通知观察者更新数据。
·具体观察者(ConcreteObserver):具体观察者是实现观察者接口的类的一个实例。具体观察者包含可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到主题主题的集合中,是自己成为他的观察者;或从具体主题中删除自己,是自己不再是观察者。
5.UML图
6.案例
案例1:一个学生(Student)关注求职中心(Center)的求职信息,在求职中心发布新的招聘信息是,学生要及时获得信息。
代码:
1 package 观察者模式.test1; 2 3 /** 4 * 主题 5 */ 6 public interface Subject { 7 public void addObserver(Observer o); 8 public void deleteObserver(Observer o); 9 public void notifyObservers(); 10 } 11 12 13 package 观察者模式.test1; 14 15 /** 16 * 观察者 17 */ 18 public interface Observer { 19 public void observe(String mess); 20 } 21 22 23 package 观察者模式.test1; 24 25 import java.util.ArrayList; 26 27 /** 28 * 具体主题 29 */ 30 public class JobCenter implements Subject { 31 String mess; //信息 32 boolean changed; 33 ArrayList<Observer> personList; //存放观察者引用 34 JobCenter(){ 35 personList = new ArrayList<Observer>(); 36 mess = ""; 37 changed = false; 38 } 39 public void addObserver(Observer o) { 40 if(!(personList.contains(o))) 41 personList.add(o); 42 } 43 44 public void deleteObserver(Observer o) { 45 if(!(personList.contains(o))) 46 personList.remove(o); 47 } 48 49 public void notifyObservers() { 50 if(changed) 51 for(Observer o : personList) 52 o.observe(mess); 53 changed = false; 54 } 55 56 public void giveNewMess(String str){ 57 if(str.equals(mess)) 58 changed = false; 59 else{ 60 mess = str; 61 changed = true; 62 } 63 } 64 } 65 66 67 package 观察者模式.test1; 68 69 /** 70 * 具体观察者 71 */ 72 public class Student implements Observer { 73 Subject subject; 74 public Student(Subject subject) { 75 this.subject = subject; 76 subject.addObserver(this); 77 } 78 79 public void observe(String mess) { 80 System.out.println("我是Student,观察如下内容:" + "\n" + mess); 81 } 82 83 } 84 85 86 package 观察者模式.test1; 87 88 public class test { 89 90 public static void main(String[] args) { 91 JobCenter center = new JobCenter();//建立具体主题 92 Student s1 = new Student(center);//建立具体观察者 93 center.giveNewMess("腾讯需要10个JAVA程序员"); 94 center.notifyObservers(); 95 center.giveNewMess("阿里需要9个JAVA工程师"); 96 center.notifyObservers(); 97 } 98 99 }
案例2:观察者模式中的“推数据”和“拉数据”
·推数据方式
推数据方式是指具体主题变化后的数据全部交给具体观察者,即将变化后的数据传递给观察者用于更新数据。当具体主题知道具体观察者需要哪些数据时使用推数据方式。
·拉数据方式
拉数据方式是指主题数据变化后不将数据传递给具体观察者,而是提供获得这些数据的方法,观察者调用这些方法得到数据,需要自己判断数据是否发生变化。当具体主题不知道具体观察者需要哪些数据时,使用拉数据方式。
一家商店(ShopSubject)每天发布当天打折商品的名字,打折前后的价格。两位顾客(Observer)对此感兴趣,其中一位只关心打折商品的名称,另一位只关心打折前后的价格。
代码:
1 package 观察者模式.test2; 2 3 /** 4 * 主题 5 */ 6 public interface Subject { 7 public void addObserver(Observer o); 8 public void deleteObserver(Observer o); 9 public void notifyObservers(); 10 } 11 12 13 package 观察者模式.test2; 14 15 /** 16 * 观察者 17 */ 18 public interface Observer { 19 public void update(); 20 } 21 22 23 package 观察者模式.test2; 24 25 import java.util.ArrayList; 26 27 /* 28 * 具体主题 29 */ 30 public class ShopSubject implements Subject { 31 String goodsName; 32 double oldPrice; 33 double newPrice; 34 ArrayList<Observer> customerList; 35 ShopSubject(){ 36 this.customerList = new ArrayList<Observer>(); 37 } 38 public void addObserver(Observer o) { 39 if(!customerList.contains(o)) 40 customerList.add(o); 41 } 42 43 public void deleteObserver(Observer o) { 44 if(customerList.contains(o)) 45 customerList.remove(o); 46 } 47 48 public void notifyObservers() { 49 for(Observer o : customerList) 50 o.update(); //然观察者执行更新操作,不提供数据 51 } 52 53 public void setDiscountGoods(String name, double oldPrice, double newPrice){ 54 goodsName = name; 55 this.oldPrice = oldPrice; 56 this.newPrice = newPrice; 57 this.notifyObservers(); 58 } 59 public String getGoodsName() { 60 return goodsName; 61 } 62 63 public double getOldPrice() { 64 return oldPrice; 65 } 66 67 public double getNewPrice() { 68 return newPrice; 69 } 70 71 } 72 73 74 package 观察者模式.test2; 75 76 /* 77 * 具体观察者1 78 */ 79 public class Customer1 implements Observer { 80 Subject subject; 81 String goodsName, personName; 82 Customer1(Subject subject, String personName){ 83 this.subject = subject; 84 this.personName = personName; 85 subject.addObserver(this); 86 } 87 public void update() { 88 if(subject instanceof ShopSubject){ 89 this.goodsName = ((ShopSubject)subject).getGoodsName(); 90 System.out.println(this.personName + "关心打折的商品名字:" + goodsName); 91 } 92 } 93 94 } 95 96 /* 97 * 具体观察者2 98 */ 99 class Customer2 extends Customer1{ 100 double oldPrice; 101 double newPrice; 102 Customer2(Subject subject, String personName) { 103 super(subject, personName); 104 } 105 public void update() { 106 if(subject instanceof ShopSubject){ 107 this.oldPrice = ((ShopSubject)subject).getOldPrice(); 108 this.newPrice = ((ShopSubject)subject).getNewPrice(); 109 System.out.println(this.personName + "关心商品打折前的价格:" + oldPrice); 110 System.out.println(this.personName + "关心商品打折后的价格:" + newPrice); 111 } 112 } 113 } 114 115 116 package 观察者模式.test2; 117 118 public class test { 119 120 public static void main(String[] args) { 121 ShopSubject shop = new ShopSubject(); 122 Customer1 c1 = new Customer1(shop, "张三"); 123 Customer2 c2 = new Customer2(shop, "李四"); 124 shop.setDiscountGoods("iPhone7", 8000, 7000); 125 shop.setDiscountGoods("小米4", 4000, 3000); 126 } 127 128 }
案例3:观察者与多主题
一个观察者可以依赖多个主题,在种情况下,使用拉数据方式。
李先生(Observer)希望知道气象站(WeatherStation)每日的天气数据和旅行社(TravelAgency)的旅行信息。
代码:
1 package 观察者模式.test3; 2 3 import java.util.ArrayList; 4 5 public class test { 6 7 public static void main(String[] args) { 8 WeatherStation w = new WeatherStation(); 9 TravelAgency t = new TravelAgency(); 10 Person p = new Person(w, t); 11 w.giveMess("10月2日", "阴有小雨", 28, 20); 12 t.giveMess("10月3日", "长城1日游"); 13 14 } 15 16 } 17 18 /** 19 * 主题 20 */ 21 interface Subject { 22 public void addObserver(Observer o); 23 public void deleteObserver(Observer o); 24 public void notifyObservers(); 25 } 26 27 /** 28 * 观察者 29 */ 30 interface Observer { 31 public void update(Subject subject); 32 } 33 34 class Theme implements Subject{ 35 private ArrayList<Observer> personList; 36 Theme(){ 37 this.personList = new ArrayList<Observer>(); 38 } 39 public void addObserver(Observer o) { 40 if(!(personList.contains(o))) 41 personList.add(o); 42 if(o == null) 43 return; 44 } 45 46 public void deleteObserver(Observer o) { 47 if(!(personList.contains(o))) 48 personList.remove(o); 49 } 50 51 public void notifyObservers() { 52 for(Observer o : personList) 53 o.update(this); 54 } 55 56 } 57 /* 58 * 具体观察者,天气预报中心 59 */ 60 class WeatherStation extends Theme{ 61 private String forecastTime, forecastMess; 62 private int maxTemperature, minTemperature; 63 64 public void giveMess(String t, String mess, int max, int min){ 65 this.forecastTime = t; 66 this.forecastMess = mess; 67 this.maxTemperature = max; 68 this.minTemperature = min; 69 this.notifyObservers(); 70 } 71 72 public String getForecastTime() { 73 return forecastTime; 74 } 75 76 public String getForecastMess() { 77 return forecastMess; 78 } 79 80 public int getMaxTemperature() { 81 return maxTemperature; 82 } 83 84 public int getMinTemperature() { 85 return minTemperature; 86 } 87 } 88 89 /* 90 * 具体主题,旅游部门 91 */ 92 class TravelAgency extends Theme{ 93 private String tourStartTime,mess; 94 95 public String getTourStartTime() { 96 return tourStartTime; 97 } 98 99 public String getMess() { 100 return mess; 101 } 102 103 public void giveMess(String time, String mess){ 104 this.tourStartTime = time; 105 this.mess = mess; 106 this.notifyObservers(); 107 } 108 } 109 110 /* 111 * 具体观察者 112 */ 113 class Person implements Observer{ 114 Subject s1,s2; 115 private String forecastTime, forecastMess; 116 private int maxTemperature, minTemperature; 117 private String tourStartTime,mess; 118 Person(Subject s1, Subject s2){ 119 this.s1 = s1; 120 this.s2 = s2; 121 s1.addObserver(this); 122 s2.addObserver(this); 123 } 124 public void update(Subject subject) { 125 if(subject instanceof WeatherStation){ 126 this.forecastTime = ((WeatherStation)subject).getForecastTime(); 127 this.forecastMess = ((WeatherStation)subject).getForecastMess(); 128 this.maxTemperature = ((WeatherStation)subject).getMaxTemperature(); 129 this.minTemperature = ((WeatherStation)subject).getMinTemperature(); 130 System.out.println("天气预报日期:" + forecastTime + "\t天气状况:" + forecastMess + "\t高该温度:" + maxTemperature + "\t最低温度:" + minTemperature); 131 } 132 if(subject instanceof TravelAgency){ 133 this.tourStartTime = ((TravelAgency)subject).getTourStartTime(); 134 this.mess = ((TravelAgency)subject).getMess(); 135 System.out.println("旅游开始日期:" + tourStartTime + "\t旅游信息:" + mess); 136 } 137 } 138 }
7.JAVA API中的Observable类和Observer接口
·作用:
由于观察者模式是JAVA程序设计中常用的模式之一,java.util包提供了用来设计符合观察者模式的Observable类和Observer接口。其中Observable类的子类称作一个具体的“可观察者”,Observer接口与上文所述的观察者接口相同。
·优点
使用观察者模式之前,统一使用java.util包中的Observable类和Observer接口将来有利于系统间的复用,避免了不同开发人员开发不同接口带来的麻烦。
·缺点
Observable是一个类不是一个接口,他的子类无法通过继承复用其他类的方法。
这个自己在网上搜案例吧。