观察者模式是典型的发布订阅模式,当一个东西有变化了,就通知所有订阅他的人
观察者模式的本质:触发联动。
当修改目标对象的状态的时候,就会触发相应的通知,然后会循环调用所有注册的观察者对象的相应方法,其实就相当于联动调用这些观察者的方法。
而且这个联动还是动态的,可以通过注册和取消注册来控制观察者,因而可以在程序运行期间,通过动态地控制观察者,来变相地实现添加和删除某些功能处理,这些功能就是观察者在update的时候执行的功能。
同时目标对象和观察者对象的解耦,又保证了无论观察者发生怎样的变化,目标对象总是能够正确地联动过来。
理解这个本质对我们非常有用,对于我们识别和使用观察者模式有非常重要的意义,尤其是在变形使用的时候。万变不离其宗。
建议在以下情况中选用观察者模式。
当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化,那么就可以选用观察者模式,将这两者封装成观察者和目标对象,当目标对象变化的时候,依赖于它的观察者对象也会发生相应的变化。这样就把抽象模型的这两个方面分离开了,使得它们可以独立地改变和复用。
如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变,这种情况可以选用观察者模式,被更改的那一个对象很明显就相当于是目标对象,而需要连带修改的多个其他对象,就作为多个观察者对象了。
当一个对象必须通知其他的对象,但是你又希望这个对象和其他被它通知的对象是松散耦合的。也就是说这个对象其实不想知道具体被通知的对象。这种情况可以选用观察者模式,这个对象就相当于是目标对象,而被它通知的对象就是观察者对象了。
观察者模式具有以下优点。
观察者模式实现了观察者和目标之间的抽象耦合
原本目标对象在状态发生改变的时候,需要直接调用所有的观察者对象,但是抽象出观察者接口以后,目标和观察者就只是在抽象层面上耦合了,也就是说目标只是知道观察者接口,并不知道具体的观察者的类,从而实现目标类和具体的观察者类之间解耦。
观察者模式实现了动态联动
所谓联动,就是做一个操作会引起其他相关的操作。由于观察者模式对观察者注册实行管理,那就可以在运行期间,通过动态地控制注册的观察者,来控制某个动作的联动范围,从而实现动态联动。
观察者模式支持广播通信
由于目标发送通知给观察者是面向所有注册的观察者,所以每次目标通知的信息就要对所有注册的观察者进行广播。当然,也可以通过在目标上添加新的功能来限制广播的范围。
在广播通信的时候要注意一个问题,就是相互广播造成死循环的问题。比如A和B两个对象互为观察者和目标对象,A对象发生状态变化,然后A来广播信息,B对象接收到通知后,在处理过程中,使得B对象的状态也发生了改变,然后B来广播信息,然后A对象接到通知后,又触发广播信息……,如此A引起B变化,B又引起A变化,从而一直相互广播信息,就造成死循环。
观察者模式的缺点是:
模拟:手机降价通知用户
1.目标类
/*** @description:通用目标接口,也可为抽象类*/
public class Subject {/*** 维护一个观察者列表*/private List list=new ArrayList<>();/*** 注册观察者* @param observer*/public void attach(Observer observer){list.add(observer);}/*** 移除观察者* @param observer*/public void detach(Observer observer){list.remove(observer);}/*** 通知观察者*/public void notifyObservers(){list.stream().forEach(obj->obj.update(this));}
}/*** @description:手机目标对象*/
@Getter
@ToString
public class PhoneSubject extends Subject{/*** 手机价格*/private double price;public void setPrice(double price) {this.price = price;//价格小于150为降价if (price<150){//通知用户降价notifyObservers();}}
}
2.观察者类
/*** @description:观察者接口*/
public interface Observer {/*** 被通知的方法* @param subject 目标对象*/void update(Subject subject);
}/*** @description:用户(观察者)*/
@Data
public class UserObserver implements Observer{/*** 用户名*/private String name;@Overridepublic void update(Subject subject) {System.out.println(this.name +"收到了降价通知,价格为"+((PhoneSubject)subject).getPrice());}
}
3.测试类
/*** @description:测试类*/
public class Client {public static void main(String[] args) {UserObserver userObserver1 = new UserObserver();userObserver1.setName("张三");UserObserver userObserver2 = new UserObserver();userObserver2.setName("李四");PhoneSubject subject=new PhoneSubject();//初始价格为150subject.setPrice(150);//绑定观察者subject.attach(userObserver1);subject.attach(userObserver2);//涨价不通知subject.setPrice(250);//降价才通知用户,第一次降价subject.setPrice(100);//降价才通知用户,第二次降价subject.setPrice(88);}
}
4.效果
1.目标类继承Observable接口即可
/*** @description:手机目标对象*/
@Getter
@ToString
public class PhoneSubject2 extends Observable {/*** 手机价格*/private double price;public void setPrice(double price) {this.price = price;//价格小于150为降价if (price<150){//通知用户降价notifyObservers();}}
}
里面维护了观察者对象,以及注册、移除观察者,通知观察者等
2.观察者类实现Observer接口即可
/*** @description:用户1*/
@Data
public class UserObserver2 implements Observer {/*** 用户名*/private String name;@Overridepublic void update(Observable o, Object arg) {if (o instanceof PhoneSubject2){System.out.println(this.name +"收到了降价通知,价格为"+((PhoneSubject2)o).getPrice());}}
}
和手写的一模一样
3.测试类
/*** @author conggq* @description:测试类* @createTime 2022/11/29 17:27*/
public class Client {public static void main(String[] args) {UserObserver userObserver1 = new UserObserver();userObserver1.setName("张三");UserObserver userObserver2 = new UserObserver();userObserver2.setName("李四");PhoneSubject2 subject2=new PhoneSubject2();//初始价格为150subject2.setPrice(150);//涨价不通知subject.setPrice(250);//降价才通知用户,第一次降价subject.setPrice(100);//降价才通知用户,第二次降价subject.setPrice(88);}
}
结果一样