01_观察者设计模式
一、代码实例
仓库地址
https://github.com/dianjiu/design-pattern
https://gitee.com/dianjiu/design-pattern
二、什么是观察者模式?
观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者自动更新状态。Java已经提供了对观察者Observer模式的默认实现, Java对观察者模式的支持主要体现在Observable类和Observer接口。
三、模式理解
当一个对象改变需要同时改变其他对象,而且他不知道具体有多少对象需要改变的时候,考虑使用观察者模式。
四、应用场景
天气变化
超市活动价
黄金价波动
五、代码实现(以黄金价格变化为例)
5.1黄金总店(主题对象)
package co.dianjiu.learn.behavior.observer.jdk8;
import java.util.Observable;
/**
* 黄金连锁总店
*
* 主题对象
*
* 继承Observable类
*/
public class GoldHeadObservable extends Observable {
//产品名称
private String productName;
//产品价格
private Double productPrice;
public GoldHeadObservable() {
}
public GoldHeadObservable(String productName, Double productPrice) {
this.productName = productName;
this.productPrice = productPrice;
}
public String getProductName() {
return productName;
}
public Double getProductPrice() {
return productPrice;
}
public void setProductName(String productName) {
this.productName = productName;
}
public void setProductPrice(Double productPrice) {
this.productPrice = productPrice;
//设置产品价格的状态已经被改变
this.setChanged();
//通知所有分店
this.notifyObservers(productPrice);
}
}
5.2黄金分店(观察者对象)
package co.dianjiu.learn.behavior.observer.jdk8;
import java.util.Observable;
import java.util.Observer;
/**
* 黄金连锁各分店
*
* 观察者
*
* 实现Observer接口
*/
public class GoldBranchObserver implements Observer {
private String name;
public GoldBranchObserver() {
}
public GoldBranchObserver(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof GoldHeadObservable && arg instanceof Double) {
GoldHeadObservable goldHeadObservable = (GoldHeadObservable) o;
Double price = (Double) arg;
System.out.println("您好:" + this.name + " ," + goldHeadObservable.getProductName()
+ "的价格已经发生改变,现在的价格为:" + price + "元/克");
}
}
}
5.3测试实例
package co.dianjiu.learn.behavior.observer.jdk8;
public class Test {
public static void main(String[] args) {
//初始化黄金连锁店 黄金产品的价格为480元每克
GoldHeadObservable goldHeadObservable = new GoldHeadObservable("黄金", 480.00);
//创建 N 个分店,并把分店添加为观察者
GoldBranchObserver jiujiang = new GoldBranchObserver("九江分店");
GoldBranchObserver fuyang = new GoldBranchObserver("阜阳分店");
goldHeadObservable.addObserver(jiujiang);
goldHeadObservable.addObserver(fuyang);
//黄金价格波动后的价格为420元每克
goldHeadObservable.setProductPrice(420.00);
}
}
5.4测试截图
六、Observer和Observable废弃
6.1废弃原因
Observer和Observable在Java 9标记为废弃有几个原因:
不能序列化
Observable没有实现Serializable接口,它的内部成员变量都是私有的,子类不能通过继承它来对Observable的成员变量处理。所以子类也不能序列化。
不是线程安全
在 java.util.Observable文档里没有强制要求Observable是线程安全的,它允许子类覆盖重写Observable的方法,事件通知无序以及事件通知发生在不同的线程里,这些都是会影响线程安全的问题。
支持事件模型的功能简单
支持事件模型的功能很简单,例如,只是支持事情发生变化的概念,但是不能提供更多哪些内容发生了改变。
6.2解决方案
可以使用java.beans 里的PropertyChangeEvent 和 PropertyChangeListener 来代替目前Observer和Observable的功能。
七、JDK9+的观察者模式使用
7.1黄金总店
package co.dianjiu.learn.behavior.observer.jdk9;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
/**
* 黄金连锁总店
*
* 主题对象
*
* 依赖属性PropertyChangeSupport
*/
public class GoldHeadObservable {
//产品名称
private String productName;
//产品价格
private Double productPrice;
private PropertyChangeSupport listeners = new PropertyChangeSupport(this);
public GoldHeadObservable() {
}
public GoldHeadObservable(String productName, Double productPrice) {
this.productName = productName;
this.productPrice = productPrice;
}
public String getProductName() {
return productName;
}
public Double getProductPrice() {
return productPrice;
}
public void setProductName(String productName) {
this.productName = productName;
}
public void setProductPrice(Double productPrice) {
String oldValue = String.valueOf(this.productPrice);
this.productPrice = productPrice;
String newValue = String.valueOf(productPrice);
//发布监听事件
firePropertyChange("productPrice",oldValue,newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener){
listeners.removePropertyChangeListener(listener);
}
protected void firePropertyChange(String prop, Object oldValue, Object newValue) {
listeners.firePropertyChange(prop, oldValue, newValue);
}
}
7.2黄金分店
package co.dianjiu.learn.behavior.observer.jdk9;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* 黄金连锁各分店
*
* 观察者
*
* 实现PropertyChangeListener接口
*/
public class GoldBranchObserver implements PropertyChangeListener {
private String name;
public GoldBranchObserver() {
}
public GoldBranchObserver(String name) {
this.name = name;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
String propertyName = evt.getPropertyName();
String oldPrice = (String) evt.getOldValue();
String newPrice = (String) evt.getNewValue();
System.out.println("您好:" + this.name + " ," + propertyName
+ "的价格已经发生改变,原来的价格为:"+oldPrice+"元/克,现在的价格为:" + newPrice + "元/克");
}
}
7.3测试实例
package co.dianjiu.learn.behavior.observer.jdk9;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class Test {
public static void main(String[] args) {
//初始化黄金连锁店 黄金产品的价格为480元每克
GoldHeadObservable goldHeadObservable = new GoldHeadObservable("黄金", 480.00);
//创建 N 个分店,并把分店添加为观察者
GoldBranchObserver jiujiang = new GoldBranchObserver("九江分店");
GoldBranchObserver fuyang = new GoldBranchObserver("阜阳分店");
goldHeadObservable.addPropertyChangeListener(jiujiang);
goldHeadObservable.addPropertyChangeListener(fuyang);
//黄金价格波动后的价格为420元每克
goldHeadObservable.setProductPrice(420.00);
System.out.println("--------------------------------------------------");
//测试移除一个监听者后,再调价格
goldHeadObservable.removePropertyChangeListener(fuyang);
goldHeadObservable.setProductPrice(430.00);
}
}
7.4测试截图
八、学习小结
在通知所有观察者之前一定要调用 setChanged()方法来设置被观察者的状态已经被改变,这样notifyObservers()才会回调Observer的update方法进行更新。在JDK9开始Observer和Observable标记为废弃,由java.beans 里的PropertyChangeListener 和 PropertyChangeEvent 来代替,在主题对象中引入
private PropertyChangeSupport listeners = new PropertyChangeSupport(this);
监听对象,通过
listeners.firePropertyChange(prop, oldValue, newValue);
监听属性的变化。
同时可以通过
//添加观察者对象
listeners.addPropertyChangeListener(listener);
//移除观察者对象
listeners.removePropertyChangeListener(listener);
进行属性变化的观察者的添加和移除。
在监听者一方通过实现PropertyChangeListener接口,并重写propertyChange方法。