当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象;MVC 模式中的模型与视图的关系;拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价等。
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
1.一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
2.一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
3.一个对象必须通知其他对象,而并不知道这些对象是谁。
4.需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
观察者模式的主要角色如下:
1.抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
2.具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
3.抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
4.具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
观察者模式的结构图如图所示:
粉丝作为观察者,粉丝关注博主的博客,博主负责更新文章然后粉丝收到通知,实现:
/**
*
* @ClassName: Observer
* @Description: 抽象观察者
* @author: ljx
* @date: 2019年4月2日 上午10:22:47
*/
public interface Observer {
//更新
public void update(String article);
}
/**
*
* @ClassName: Fans
* @Description: 具体观察者:粉丝
* @author: ljx
* @date: 2019年4月2日 上午10:25:37
*/
public class Fans implements Observer {
@Override
public void update(String article) {
System.out.println("你关注的博主更新文章了 !文章为 :" + article);
}
}
/**
*
* @ClassName: Fans
* @Description: 具体观察者:粉丝2
* @author: ljx
* @date: 2019年4月2日 上午10:25:37
*/
public class Fans2 implements Observer{
@Override
public void update(String article) {
System.out.println("你关注的博主更新文章了 !文章为 :" + article);
}
}
/**
*
* @ClassName: Subject
* @Description: 抽象主题角色
* @author: ljx
* @date: 2019年4月2日 上午10:30:30
*/
public abstract class Subject {
//存放所有观察者对象
List list = new ArrayList();
/**
*
* @Title: addObserver
* @Description: 添加观察者
* @param observer
* @return: void
*/
public void addObserver(Observer observer){
list.add(observer);
}
/**
*
* @Title: removeObserver
* @Description: 移除观察者
* @param observer
* @return: void
*/
public void removeObserver(Observer observer){
list.remove(observer);
}
/**
*
* @Title: nodifyObserver
* @Description: 通知所有观察者
* @param article
* @return: void
*/
public void nodifyObserver(String article){
for (Observer observer : list) {
observer.update(article);
}
}
}
/**
*
* @ClassName: Article
* @Description: 具体主题角色:作者
* @author: ljx
* @date: 2019年4月2日 上午10:39:55
*/
public class Article extends Subject {
private String article;
public String getArticle() {
return article;
}
public void setArticle(String article) {
this.article = article;
//通知观察者
this.nodifyObserver(article);
}
}
public class ObserverTest {
public static void main(String[] args) {
//创建主题
Article article = new Article();
//创建观察者
Observer fans = new Fans();
Observer fans2 = new Fans2();
//创建观察者
article.addObserver(fans);
article.addObserver(fans2);
article.setArticle("今天发微博");
}
}
结果:
你关注的博主更新文章了 !文章为 :今天发微博
你关注的博主更新文章了 !文章为 :今天发微博
在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
一. Observable类
Observable 类是抽象目标类,它有一个 Vector 向量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法。
1.void addObserver(Observer o) 方法:用于将新的观察者对象添加到向量中。
2.void notifyObservers(Object arg) 方法:调用向量中的所有观察者对象的 update。方法,通知它们数据发生改变。通常越晚加入向量的观察者越先得到通知。
3.void setChange() 方法:用来设置一个 boolean 类型的内部标志位,注明目标对象发生了变化。当它为真时,notifyObservers() 才会通知观察者。
二. Observer 接口
Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 void update(Observable o,Object arg) 方法,进行相应的工作。
利用 Observable 类和 Observer 接口实现原油期货的观察者模式实例:
/**
*
* @ClassName: Bull
* @Description: 多方
* @author: ljx
* @date: 2019年4月2日 上午11:01:49
*/
public class Bull implements Observer {
@Override
public void update(Observable o, Object arg) {
Float price = ((Float)arg).floatValue();
if(price > 0){
System.out.println("油价上涨" + price + "元,多方高兴了!");
}else{
System.out.println("油价下跌"+(-price)+"元,多方伤心了!");
}
}
}
/**
*
* @ClassName: Bear
* @Description: 空方
* @author: ljx
* @date: 2019年4月2日 上午11:01:58
*/
public class Bear implements Observer {
@Override
public void update(Observable o, Object arg) {
Float price = ((Float) arg).floatValue();
if (price > 0) {
System.out.println("油价上涨" + price + "元,空方伤心了!");
} else {
System.out.println("油价下跌" + (-price) + "元,空方高兴了!");
}
}
}
/**
*
* @ClassName: OilFutures
* @Description: 原油期货
* @author: ljx
* @date: 2019年4月2日 上午11:01:35
*/
public class OilFutures extends Observable {
private Float price;
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
super.setChanged();//设置内部标志位,注明数据发生变化
super.notifyObservers(price);//通知观察者价格改变了
}
}
public class CrudeOilFutures {
public static void main(String[] args) {
//创建观察者
Observer bear = new Bear();
Observer bull = new Bull();
//创建主题者
OilFutures futures = new OilFutures();
//添加观察者
futures.addObserver(bear);
futures.addObserver(bull);
futures.setPrice(3.23f);
futures.setPrice(-2.97f);
}
}
结果:
油价上涨3.23元,多方高兴了!
油价上涨3.23元,空方伤心了!
油价下跌2.97元,多方伤心了!
油价下跌2.97元,空方高兴了!
... ...,欢迎指正!