当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
应用场景
当一个对象的数据更新时需要通知其他对象,但这个对象又不希望和被通知的那些对象形成紧耦合。
模式结构
1.主题(Subject):是一个接口,定义了具体主题需要实现的方法,比如:添加观察者、通知所有观察者。
2.观察者(Observer):是一个接口,定义了观察者收到通知后做出应对的方法
3.具体主题(ConcreteSubject):实现Subject接口,类中包含一个存放观察者的 List
4.具体观察者(ConcreteObserver):类中声明了一个 Subject 类型成员变量,用来做订阅(addObserver)和取消订阅(deleteObserver)。
笔者心得:笔者刚开始学这个的时候不理解为什么需要有一个 Subject 引用,直接在 Subject 某子类的实例中addObserver 和 deleteObserver 不就好了?如果是这样,这种模式的主动权在被观察者这里,被观察者想被谁观察就 add ,不想被谁观察就 delete ,实际上不恰当。主动权应该在观察者手里,观察者去选择订阅或者取消订阅。
代码案例
文中举了一个拍卖会的例子,首先,创建 Subject 接口和 Observer 接口:
public interface Subject {
void addObserver(Observer observer);
void deleteObserver(Observer observer);
void notifyObservers();
}
public interface Observer {
/**
* 拍卖
* */
void auction(String message);
}
创建拍卖会现场类:
public class AuctionInstitution implements Subject{
private List<Observer> observers = new ArrayList<>();
private String message;
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void deleteObserver(Observer observer) {
if (observers.contains(observer)) {
observers.remove(observer);
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.auction(message);
}
}
public void setMessage(String message) {
System.out.println(message);
this.message = message;
notifyObservers();
}
}
创建拍卖者A与拍卖者B:
public class TraderA implements Observer{
Subject subject;
TraderA(Subject subject) {
this.subject = subject;
subject.addObserver(this);
}
@Override
public void auction(String message) {
if (message.contains("宝石")) {
System.out.println("TraderA:我要买宝石!");
} else {
System.out.println("TraderA:我只对宝石感兴趣!");
}
}
}
public class TraderB implements Observer{
Subject subject;
TraderB(Subject subject) {
this.subject = subject;
subject.addObserver(this);
}
@Override
public void auction(String message) {
System.out.println("TraderB:我比较喜欢收藏各种物件,果断入手!");
}
public void toilet() {
System.out.println("TraderB:去趟洗手间,暂停收听一会儿~");
subject.deleteObserver(this);
}
}
创建测试类:
public class AuctionTest {
public static void main(String[] args) {
AuctionInstitution auctionInstitution = new AuctionInstitution();
TraderA traderA = new TraderA(auctionInstitution);
TraderB traderB = new TraderB(auctionInstitution);
auctionInstitution.setMessage("第一件,拍卖水晶!");
System.out.println("------------------------------");
traderB.toilet();
auctionInstitution.setMessage("第二件,拍卖宝石!");
}
}
运行结果:
优点
1. 具体主题和具体观察者是松耦合关系。主题接口仅依赖于观察者接口,具体主题不需要知道具体观察者是哪个类。同样,由于观察者仅依赖于主题接口,因此具体观察者只是知道它依赖的主题是实现主题接口的某个类的实例,但不需要知道具体是哪个类。
2. 观察者模式满足“开-闭原则”。主题接口仅依赖于观察者接口,这样,就可以让创建具体主题的类也仅是依赖于观察者接口,因此,如果增加新的实现观察者接口的类,不必修改创建具体主题的类的代码。同样,创建具体观察者的类仅仅依赖于主题接口,如果增加新的实现主题接口的类,也不必修改创建具体观察者类的代码。
缺点
1. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。(这样看来,观察者更应该叫订阅者或者监听者)。