引入:
观察者模式定义了一种一对多 的依赖关系,让多个观察者对象同时监视着被观察者的状态,当被观察者的状态发生变化 时, 会通知所有观察者, 并让其自动更新自己。
在现实中有些对象数据发生了变化,则这些对象也需要发生变化。举个例子, 一个商家有一些产品,它和一些电商合作,每当有新产品时,就会把这 些产品推送到电商,现在只和淘宝、京东合作。
if (产品库有新产品) {
推送产品到淘宝;
推送产品到京东;
}
但如果电商多了,就需要添加更多的“推送产品到...”,这样复杂且耦合度高。
这是观察者模式就出场了。首先, 把每一个电商接口看成一个观察 者,每一个观察者都能观察到产品列表(被监听对象)。当公司发布新产品时,就会发送到 这个产品列表上, 于是产品列表(被监昕对象)就发生了变化,这时就可以触发各个电商 接口(观察者〉发送新产品到对应的合作电商那里。
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import com.observer.Observer;
//被观察者列表清单
public class ProductList extends Observable{
private List<String> productList = null;// 产品列表
private static ProductList instance;// 类唯一实例
private ProductList() {
}// 构造方法私有化
/**
* 取得唯一实例
* @return 产品列表唯一实例
*/
public static ProductList getInstance() {
if (instance == null) {
instance = new ProductList();
instance.productList = new ArrayList<String>();
}
return instance;
}
/**
* 添加观察者(电商接口)
* @param observer 观察者
*/
public void addProductListObserver(Observer observer){
this.addProductListObserver(observer);
}
public void addProduct(String newProduct){
productList.add(newProduct);
System.out.println("产品列表新增了产品:"+newProduct);
this.setChanged();//设置被观察对象发生变化
this.notifyObservers(newProduct);//通知观察者,并传递新产品
}
}
import java.util.Observable;
import java.util.Observer;
//京东电商接口
public class JingDongObserver implements Observer{
@Override
public void update(Observable o, Object product) {
String newProduct=(String)product;
System.out.println("发送新产品 "+newProduct+ " 同步到京东商城");
}
}
//淘宝电商接口
....
public class Test {
public static void main(String[] args) {
ProductList observable = ProductList.getInstance();
TaoBaoObserver taoBaoObserver = new TaoBaoObserver();
JingDongObserver jingDongObserver = new JingDongObserver();
observable.addObserver(taoBaoObserver);
observable.addObserver(jingDongObserver);
observable.addProduct("新增产品1");
}
}
观察者模式
UML图:
代码实现:
/*
* 观察者接口类IObserver
*/
public interface IObserver {
public void refresh(String data);
}
/*
* 主题接口类ISubject
*/
public interface ISubject {
public void register(IObserver obs); //注册观察者
public void unregister(IObserver obs);//撤销观察者
public void notifyObservers(); //通知所有观察者
}
/*
* 主题实现类Subject
*/
public class Subject implements ISubject {
// 观察者维护向量
private Vector<IObserver> vec = new Vector<>();
private String data; // 主题中心数据
public String getData() {
return data;
}
public void setData(String data) {// 主题注册
this.data = data;
}
@Override
public void register(IObserver obs) {// 主题注册(添加)观察者
vec.add(obs);
// TODO Auto-generated method stub
}
@Override
public void unregister(IObserver obs) {// 主题撤销(删除)观察者
if (vec.contains(obs))
vec.remove(obs);
}
@Override
public void notifyObservers() {// 主题通知所有观察者进行数据响应
for (int i = 0; i < vec.size(); i++) {
IObserver obs = vec.get(i);
obs.refresh(data);
}
}
}
/*
* 一个具体观察者类 Observer
*/
public class Observer implements IObserver{
@Override
public void refresh(String data) {
System.out.println("I have received the data: "+data);
}
}
public class Test {
public static void main(String[] args) {
IObserver obs = new Observer();// 定义观察者对象
Subject subject = new Subject();//定义主题对象
subject.register(obs);//主题增加观察者
subject.setData("Hello");//主题中心数据变动了
subject.notifyObservers();//通知所有观察者进行数据响应
}
}
深入观察者模式
(1)主题接口类ISubject和观察者接口类IObserver 可以改为泛型接口更通用。
(2)数据传递的两种方式“推”和“拉”
推数据的方式是指主题对象直接将数据传送给观察者对象。(上面那种就是推)
而拉数据则是观察者对象可间接获得变化后的主题数据。
public interface IObserver<T> {
public void refresh(ISubject<T> obj);
}
public class Subject implements ISubject {
......
@Override
public void notifyObservers() {// 主题通知所有观察者进行数据响应
for (int i = 0; i < vec.size(); i++) {
IObserver obs = vec.get(i);
obs.refresh(this); //代替原来的obs.refresh(data);
}
}
}
public class Observer implements IObserver{
@Override
public void refresh(ISubject obj) {
Subject subject=(Subject)obj;
System.out.println("I have received the data: "+data);
}
}
(3)增加抽象层AbstractSubject
如果有很多个主题类,而每个主题类都要重写register(),unregister(),notifyObservers()方法。如果这三个方法的代码是相同的,那么在每个主题类中实现就让代码重复了,所以应该加个中间抽象层来解决这个问题。
jdk中的观察者设计模式
(其实第一个电商的例子中我们已经用到就是jdk中的观察者模式)
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
1.通过jdk中notifyObservers()方法的实现,可以看出它设置了一个标识变量changed,只有当changed为true时,观察者对象才能数据响应(可想而知这样也才符合常理,没有数据变化就没有必要响应。)
2.void update(Observable o, Object arg) 和 public void notifyObservers(Object arg) 两个方法中增加了arg参数对象的设定,可以把一些比较信息由主题动态传递给观察者,让编程更加灵活。