观察者模式
一、定义
观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。
二、观察者模式结构图
观察者模式结构图中的角色:
抽象主题(Subject):它是指被观察的对象。该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法。
具体主题(ConcreteSubject):该角色继承或实现了抽象主题,定义了一个集合存入注册过的具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
抽象观察者(Observer):该角色是具体观察者的抽象类,定义了一个更新方法。
具体观察者(ConcreteObserver):该角色是具体的观察者对象,在得到具体主题更改通知时更新自身的状态。
观察者模式的结构:Subject(被观察者或者说是目标类):要有添加观察者,删除观察者,和提醒观察者(当被观察者的状态发生改变的时候调用这个方法)的方法,Observe(观察者):要有更新方法(当观察者状态发生改变调用提醒方法后观察者通过更新方法来做出不同响应(动作))。
ConcreteSubject是Subject接口的实现类
ConcreteObserver是Observer接口的实现类
/**
* 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口
*/
public class Subject {
// 用来保存注册的观察者对象
private List<Observer> observers = new ArrayList<Observer>();
// attach detach notifyObservers
// 把订阅天气的人添加到订阅者列表中
public void attach(Observer observer) {
observers.add(observer);
}
/**
* 删除集合中的指定观察者
* @param observer
*/
public void detach(Observer observer) {
observers.remove(observer);
}
/**
* 通知所有注册的观察者对象
*/
protected void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
}
/**
* 具体的目标对象,负责把有关状态存入到相应的观察者对象中
*/
public class ConcreteSubject extends Subject {
// 目标对象的状态
private String Content;
public String getSubjectState() {
return Content;
}
public void setSubjectState(String Content) {
this.Content = Content;
// 内容有了,通知所有的订阅的人
this.notifyObservers();
}
}
/**
* 这是一个观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
*/
public interface Observer {
/**
* 更新的接口 传入目标对象,方便获取相应的目标对象的状态
*/
void update(Subject subject);
}
/**
* 具体的观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
*/
public class ConcreteObserver implements Observer {
// 观察者的名字,是谁收到了这个讯息
private String observerName;
// 观察者的状态,这个消息从目标处获取
private String observerState;
// 提醒的内容
private String remindTing;
/**
* 获取目标类的状态同步到观察者的状态中
*/
@Override
public void update(Subject subject) {
observerState = ((ConcreteSubject) subject).getSubjectState();
System.out.println(observerName + "收到了, " + observerState + " , " + remindTing);
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
public String getRemindTing() {
return remindTing;
}
public void setRemindTing(String remindTing) {
this.remindTing = remindTing;
}
}
public static void main(String[] args) {
// 1.创建目标
ConcreteSubject weather = new ConcreteSubject();
// 2.创建观察者
ConcreteObserver observerGirl = new ConcreteObserver();
observerGirl.setObserverName("小明的女朋友");
observerGirl.setRemindTing("是我们的第一次约会,地点街心公园,不见不散哦");
ConcreteObserver observerMum = new ConcreteObserver();
observerMum.setObserverName("老妈");
observerMum.setRemindTing("是一个购物的好日子,明天去天虹扫货");
// 3.注册观察者
weather.attach(observerGirl);
weather.attach(observerMum);
// 4.目标发布天气
weather.setSubjectState("#明天天气晴朗,蓝天白云,气温28度#");
}
三、观察者模式优缺点
观察者模式优点:
- 观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色。
- 观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
- 观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度。
- 观察者模式满足“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。
观察者模式缺点:
- 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
适用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。