在软件开发中,观察者模式是使用频率最高的设计模式之一,如果你做过web开发,对它应该更不会陌生,因为典型的MVC架构就是对观察者模式的一种延伸。在软件开发中经常会碰到这种困境:系统由若干个相互协作的类构成,类之间常有一对多的依赖关系,当被依赖对象的状态变化时,其他所有依赖对象都要发生改变。以MVC为例,模型(Model)对象封装了数据,视图(View)对象对数据进行渲染和进行图形表示。当模型中的数据改变时,视图应该马上得到反馈从而改变其显示的内容。我们需要维护这种具有依赖关系的对象之间的一致性,又不希望为了维护这种一致性导致类之间紧密耦合。而观察者模式正式对这一困境的回答。观察者模式的的最大好处是可以实现具有关联关系的对象之间的解耦,使得双方可以独立的进行扩展和变化,使得系统具有更好的弹性。
观察者模式定义了对象之间的一对多依赖关系,每当对象改变状态,所有依赖于它的对象都会得到通知并被自动更新。
观察者模式的结构相对简单,可以用<Head First设计模式>
中的一张图来描述
观察者模式的主要角色:
观察者模式又被称为发布订阅模式,以客户订阅报纸为例,客户相当于观察者,而报社则是被观察者。客户可以向报社订阅报纸,也可以取消订阅。当报社有新报纸出版时,就会将报纸发送给订阅的客户。
/**
* @author: takumiCX
* @create: 2018-10-29
**/
public abstract class Subject {
//观察者集合
private CopyOnWriteArrayList<Observer> observers=new CopyOnWriteArrayList<>();
//注册观察者
protected void registerObserver(Observer observer){
observers.add(observer);
}
//移除观察者
protected boolean removeObserver(Observer observer){
return observers.remove(observer);
}
/**
* 通知观察者
* @param msg 发送给观察者的消息
*/
protected void notifyObservers(String msg){
for(Observer observer:observers){
observer.update(msg);
}
}
/**
* 通知观察者
*/
protected void notifyObservers(){
for(Observer observer:observers){
observer.update();
}
}
}
该抽象类内部维护了一个线程安全的CopyOnWriteArrayList来存储观察者集合,并没有使用Vector或者SynchronizedList等常见同步容器,在需要频繁增删观察者的情况下可以一定程度提升性能。
/**
* @author: takumiCX
* @create: 2018-10-29
**/
public class NewsPaperSubject extends Subject {
//报纸的期号
private String date;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
/**
* 通知订阅的客户接收新一期的报纸
*/
public void postNewPublication(){
notifyObservers(date);
}
}
具体的主题实现类继承了主题抽象类,并添加了一个状态变量,表示报纸的期号,在通知订阅的客户时需要将该信息也一起传过去。postNewPublication()
方法当有新一期的报纸发行时,会通过调用该方法对订阅的客户进行通知。
/**
* @author: takumiCX
* @create: 2018-10-29
**/
public interface Observer {
void update(String msg);
void update();
}
/**
* @author: takumiCX
* @create: 2018-10-29
**/
public class CustomerObserver implements Observer {
//客户姓名
private String name;
public CustomerObserver(String name) {
this.name = name;
}
@Override
public void update(String msg) {
System.out.println(name+" 您好!"+msg+" 期的报纸已发送,请注意接收!");
}
@Override
public void update() {
}
}
/**
* @author: takumiCX
* @create: 2018-10-29
**/
public class Test {
publi