前言
观察者模式是使用频率最高的设计模式之一,当对象间存在一对多关系时候就可以使用观察者模式。
观察者模式又被称为
- 发布-订阅模式
- 模型-视图模式
- 源-监听器模式
- 从属者模式
观察者模式是一种对象行为型模式。定义对象间的一种一对多依赖关系,一个对象发生更改时候,自动通知其他对象,被通知的对象称为观察者。
例如:
一个变量的修改,需要自动通知依赖它的对象。
观察者模式结构
观察者模式结构图中主要有以下角色
- Subject(目标):指被观察的对象,其中定义了一个观察者集合,一个目标可以接受任意数量的观察者观察,提供一系列方法增加和删除观察者对象,同时还定义了通知方法。可以为接口、抽象类和具体类。
- ConcreteSubject(具体目标):目标类的子类,通常包含经常发生改变的数据,当其状态改变,向它的各个观察者发出通知,同时如何目标类中有抽象业务逻辑方法,其也会实现。如果无需扩展,则具体目标类可以省略。
- Observe(观察者):观察者对观察目标的改变做出反应,一般定义为接口,声明更新数据的方法update(),又被称为抽象观察者。
- ConcreteObserver(具体观察者):其维护一个指向具体目标对象的引用,存储具体观察者的有关状态,其状态需要和具体目标的状态保持一致,实现**update()**方法。
Java对于观察者模式的支持
在JDK的java.util包中,提供了Observable类以及Observer接口。其是从jdk1.0便开始提供的类与接口,但是在jdk9之后弃用,其api中解释如下。
所以此处不多解释。
观察者模式与MVC
MVC架构包括三个角色:
- 模型(Model)
- 视图(View)
- 控制器(Controller)
其中模型可以对应观察者模式中的观察目标,视图对应观察者,而控制器充当二者中间的中介者,模型层的数据发生改变时,视图层自动改变其显示内容。
总结
观察者模式的主要优点:
- 可以实现表示层和数据逻辑层的分离,定义稳定的消息更新传递机制,抽象更新接口,可以让各种各样不同的表示层充当具体观察者。
- 在观察目标和观察者之间建立了一个抽象的耦合,观察对象和观察者解耦合。
- 简化一对多系统设计的难度。
- 观察者模式满足“开闭原则”。
观察者模式的主要缺点:
- 如果一个观察目标对象有很多观察者,那么通知每一个观察者时间会很长。
- 若观察者与观察目标之间存在循环依赖,可能会触发循环调用,可能导致系统崩溃。
- 观察者模式只会让观察者知道目标发生了变化,而不知道发生了什么变化。
例子
在拿到一个新的需求的时候,我们先分析一下用什么设计模式,为什么要用这个设计模式。
我们可以看到这个需求中,可以抽象出以下几个角色。
- 股票购买者
- 股票
而事件是当变化幅度达到5%时候,通知所有购买了股票的股民。
显而易见,这就是一个典型的观察者模式的引用。
//客户端类
public class Client {
public static void main(String[] args) {
Subject subject1=new ConcreteSubject("股票1",100.0);
Subject subject2=new ConcreteSubject("股票2",100.0);
Subject subject3=new ConcreteSubject("股票3",100.0);
Observer obs1=new ConcreteObserver("股民1");
Observer obs2=new ConcreteObserver("股民2");
Observer obs3=new ConcreteObserver("股民3");
subject1.attachUser(obs1);
subject1.attachUser(obs3);
subject2.attachUser(obs1);
subject2.attachUser(obs2);
subject3.attachUser(obs2);
subject3.attachUser(obs3);
subject1.setValue(200.0);
subject2.setValue(300.0);
}
}
public interface Subject {
public String getName();
public void setName(String name);
public Double getValue();
public void setValue(Double val);
public void attachUser(Observer obs);
public void deleteUser(Observer obs);
}
public class ConcreteSubject implements Subject{
private String name;
private Double value;
private ArrayList<Observer> users;
@Override
public void attachUser(Observer obs){
this.users.add(obs);
}
@Override
public void deleteUser(Observer obs){
this.users.remove(obs);
}
public ConcreteSubject(String name,Double val){
this.name=name;
this.value=val;
users=new ArrayList<>();
}
@Override
public String getName() {
return this.name;
}
@Override
public void setName(String name) {
this.name=name;
}
@Override
public Double getValue() {
return this.value;
}
@Override
public void setValue(Double val) {
Double change=(val-this.value)/this.value;
if(change>=0.05){
for(Observer user:users){
user.notify(this,val,change);
}
}
this.value=val;
}
}
public interface Observer {
public void notify(Subject shares,Double aftval,Double change);
}
public class ConcreteObserver implements Observer{
private String userName;
public ConcreteObserver(String name){
this.userName=name;
}
@Override
public void notify(Subject shares, Double aftval, Double change) {
System.out.println(userName+"的股票:"+shares.getName()+"\t"+"原价为:"+shares.getValue()+";"+"现价为:"+aftval+";"+"涨幅为:"+change*100+"%");
}
}