当一个对象的某个状态改变时,随之而来的是改变其他对象的状态。比如商品降价时,要通知所有关注此商品的人,我们可能第一时间想到这样写:
/**
* @ClassName: Commodity
* @Description: 商品类
*/
public class Commodity {
//商品价格
private Double price;
public void priceChanged(){
BuyerOne buyerOne = new BuyerOne();
//通知第一位买家价格变动
buyerOne.getNewPrice(price);
BuyerTwo buyerTwo = new BuyerTwo();
//通知第二位买家价格变动
buyerTwo.getNewPrice(price);
}
}
这样写有以下几个缺点:
1. 针对具体实现编程,而非针对接口编程
2. 每一次有增加或减少通知对象,我们都要修改代码;
3. 我们没办法在程序运行时,动态地增加或减少需要通知的对象,没有弹性;
4. 严重侵犯了通知对象的封装。
为了解决避免上述缺点,采用观察者模式解决这一类问题。观察者模式(Observer Parttern),对象之间存在一对多的依赖,当一个对象的状态发生改变时,它所依赖的对象都会得到通知并随之改变,这个对象我们称之为主题(Subject),它所依赖的对象我们称之为观察者(Observer)。
主题对象管理者某些数据,当主题数据发生了改变时会通知它的观察者,观察者已经订阅了主题数据发生改变时,这些观察者也会得到改变信息,做出相应的反应。相反,不是该主题的观察者,无论主题数据如何变化,这个对象始终都接受不到消息更不会因为主题对象的改变而改变。
在上述例子中,两个买家对象都实现了同样的getNewPrice()方法,采用具体实现完成这项工作,耦合度很高。一个系统的耦合度较高时,那么这个系统的弹性就会很差,很难应付频发的业务变化。观察者模式提供了一种对象设计,将主题与观察者之间松耦合,通过针对接口来代替具体实现,主题中只要知道观察者实现了某个接口,而所有的观察者都实现这个接口,具体的实现大家互补侵犯。因为观察者实现了统一的接口,主题存储这个接口列表就可以实现在程序运行时,动态的增加或删除注册的观察者。
有了大概思路,接下来看如何将上面的例子使用观察者模式实现:
买家接口,也就是观察者接口:
/**
* @ClassName: Buyer
* @Description: 买家接口
*/
public interface Buyer {
/**
* @Title: getNewPrice
* @Description: 得到新的价格
* @param price
*/
public void getNewPrice(Double price);
}
商品类,也就是主题:
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName: Commodity
* @Description: 商品类
*/
public class Commodity {
//商品价格
private Double price;
//用来记录所有的买家(观察者列表)
private List<Buyer> buyers;
//在基础构造方法中初始化记录买家的列表
public Commodity(){
this.buyers = new ArrayList<Buyer>();
}
//买家注册,其实就是将买家变成观察者,加入到观察者列表
public void registerBuyer(Buyer buyer){
this.buyers.add(buyer);
}
//当买家不在关注此商品是,取消订阅,从观察者列表中将其删除
public void removeBuyer(Buyer buyer){
int i = this.buyers.indexOf(buyer);
if(i >= 0){
this.buyers.remove(i);
}
}
//价格状态改变,通知所有买家
//向所有订阅者发送状态改变消息
private void priceChanged(){
for(Buyer buyer : buyers){
buyer.getNewPrice(this.price);
}
}
//模拟打折活动,当调用打折方法,修改了价格,
//也就意味着主题状态改变,应该执行通知所有观察者
public void discountCommodity(Double price){
//第一步,修改价格
this.price = price;
//第二部,通知所有观察者
priceChanged();
}
}
具体的买家,也就是具体的观察者,实现观察者接口的实现类:
/**
* @ClassName: BuyerOne
* @Description: 买家1
*/
class BuyerOne implements Buyer{
//商品价格
private Double price;
@Override
public void getNewPrice(Double price) {
this.price = price;
System.out.println("BuyerOne get new price : " + price);
}
}
/**
* @ClassName: BuyerTwo
* @Description: 买家2
*/
class BuyerTwo implements Buyer{
//商品价格
private Double price;
@Override
public void getNewPrice(Double price) {
this.price = price;
System.out.println("BuyerTwo get new price : " + price);
}
}
执行测试:
public class Test {
//进行测试
public static void main(String[] args){
//创建商品
Commodity commodity = new Commodity();
//第一个买家进行关注,也就是说第一个观察者进行了注册
BuyerOne one = new BuyerOne();
commodity.registerBuyer(one);
//第二个注册
BuyerTwo two = new BuyerTwo();
commodity.registerBuyer(two);
//执行降价操作,所有观察者都应该受到信息
commodity.discountCommodity(0.2);
}
}
以下就是执行结果:
BuyerOne get new price : 0.2
BuyerTwo get new price : 0.2
由此可以看到主题发生了状态改变,达到了通知所有观察者的效果。观察者模式的简单实现就此完成。
总结:
1、观察者模式就是对象直接存在一对多的依赖,一的一方(主题)改变,要通知多的一方(观察者)
2、面向接口编程,不是面向类编程,减少代码入侵
3、程序运行时,可动态修改主题状态
4、让主题和观察者之间松耦合,弹性更好
下面一篇将介绍Java内置的观察者模式,并进行源码分析。