一、前言
前面学习了策略模式,接着学习观察者模式,观察者模式是一个很常用的模式,如订阅RSS这个功能就适合使用观察者模式来实现,园友订阅了博客园文章后,当博客园的文章有更新时,会收到相应的通知,这就是观察者模式的应用,并且JDK中都内置了对观察者模式的支持,下面来学习观察者模式。
二、观察者模式定义
定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
定义中的一通常称为主题,多称为观察者,当主题发生变化时,观察者会收到相应的变化,下面通过示例来演示观察者模式。
三、示例
若有这样的一个场景,当园友订阅博客园文章后,博客园文章更新后,程序员需要收到更新的文章。这时,使用观察者模式最合适不过了。下面是类图
说明:Subject是主题接口,包含了三个方法,CNBlog是具体的主题,表示博客园,Oberser是观察者接口,包含了update方法,在主题发生变化时,update方法会被调用,Coder是具体的观察者,表示程序员。
3.1 v1.0
根据类图,代码如下
Subject
package com.hust.grid.leesf.observer; public interface Subject { void registerObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(); }
Observer
package com.hust.grid.leesf.observer; public interface Observer { void update(String message); }
CNBlog
package com.hust.grid.leesf.observer; import java.util.ArrayList; import java.util.List; public class CNBlog implements Subject { private List<Observer> observers; private String message; public CNBlog() { observers = new ArrayList<Observer>(); } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0) { observers.remove(index); } } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(message); } } /** * 模拟博客园的文章更新了 * @param message */ public void setMessage(String message) { this.message = message; // 有更新 messageChanged(); } private void messageChanged() { // 通知观察者 notifyObservers(); } }
Coder
package com.hust.grid.leesf.observer; public class Coder implements Observer { private String name; public Coder(String name) { this.name = name; } @Override public void update(String message) { System.out.println("I am " + name + ", received updated message from subject is [" + message + "]"); } }
Main(用于测试)
package com.hust.grid.leesf.observer; /** * @since 2016/6/1 * @author LEESF * */ public class Main { public static void main(String[] args) { // 新生主题 CNBlog cnBlog = new CNBlog(); // 新生程序员 Observer leesf = new Coder("leesf"); // 向主题注册 cnBlog.registerObserver(leesf); // 新生程序员 Observer lee = new Coder("lee"); // 向主题注册 cnBlog.registerObserver(lee); // 模拟主题发生变化 cnBlog.setMessage("leesf发布了一篇新文章"); System.out.println("----------------------------------------"); // 移除观察者 cnBlog.removeObserver(lee); // 模拟主题发生变化 cnBlog.setMessage("leesf又发布了一篇新文章"); } }
运行结果
I am leesf, received updated message from subject is [leesf发布了一篇新文章] I am lee, received updated message from subject is [leesf发布了一篇新文章] ---------------------------------------- I am leesf, received updated message from subject is [leesf又发布了一篇新文章]
说明:首先,leesf与lee向CNBlog主题进行了注册,成为观察者,之后,CNBlog的信息发生变化,可以看到,此时,leesf与lee都收到了通知;之后,从主题中移除观察者lee,CNBlog的信息再次发生变化时,只有leesf收到了信息,而lee则不会收到信息。
这样的设计会使得系统极具扩展性,可以方便的添加其他主题或者观察者而不会影响之前的代码。如,可以添加一个CSDN主题、今日头条主题等,这些主题只需要实现Subject接口即可,同时,也可以添加其他类型观察者,如设计师、科学家等,这些观察者只需要实现Observer接口即可。类图如下图所示
说明:Coder向CNBlog、CSDN主题进行注册,成为它们观察者;Designer向TouTiao、CSDN主题进行注册,成为它们的观察者;Scientist想TouTiao注册,成为它的观察者。
3.2 v2.0
根据类图,代码如下
CSDN
package com.hust.grid.leesf.observer; import java.util.ArrayList; import java.util.List; public class CSDN implements Subject { private List<Observer> observers; private String message; public CSDN() { observers = new ArrayList<Observer>(); } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0) { observers.remove(index); } } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(message); } } /** * 模拟CSDN的文章更新了 * @param message */ public void setMessage(String message) { this.message = message; // 有更新 messageChanged(); } private void messageChanged() { // 通知观察者 notifyObservers(); } }
TouTiao
package com.hust.grid.leesf.observer; import java.util.ArrayList; import java.util.List; public class TouTiao implements Subject { private List<Observer> observers; private String message; public TouTiao() { observers = new ArrayList<Observer>(); } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0) { observers.remove(index); } } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(message); } } /** * 模拟头条的的新闻更新了 * @param message */ public void setMessage(String message) { this.message = message; // 有更新 messageChanged(); } private void messageChanged() { // 通知观察者 notifyObservers(); } }
Designer
package com.hust.grid.leesf.observer; public class Designer implements Observer { private String name; public Designer(String name) { this.name = name; } @Override public void update(String message) { System.out.println("I am " + name + ", received updated message from subject is [" + message + "]"); } }
Scientist
package com.hust.grid.leesf.observer; public class Scientist implements Observer { private String name; public Scientist(String name) { this.name = name; } @Override public void update(String message) { System.out.println("I am " + name + ", received updated message from subject is [" + message + "]"); } }
Main(用作测试)
package com.hust.grid.leesf.observer; /** * @since 2016/6/1 * @author LEESF * */ public class Main { public static void main(String[] args) { // 新生主题 CNBlog cnBlog = new CNBlog(); CSDN csdn = new CSDN(); TouTiao touTiao = new TouTiao(); // 新生程序员 Observer leesf = new Coder("leesf"); // 向CNBlog主题注册 cnBlog.registerObserver(leesf); // 向CSDN主题注册 csdn.registerObserver(leesf); // 新生设计师 Observer dyd = new Coder("dyd"); // 向CSDN主题注册 csdn.registerObserver(dyd); // 向头条主题注册 touTiao.registerObserver(dyd); // 新生科学家 Observer ld = new Scientist("ld"); // 向头条主题注册 touTiao.registerObserver(ld); // 模拟主题发生变化 cnBlog.setMessage("leesf在博客园上发布了一篇新文章"); System.out.println("----------------------------------------"); // 模拟主题发生变化 csdn.setMessage("leesf在CSDN上发布了一篇新文章"); System.out.println("----------------------------------------"); // 模拟主题发生变化 touTiao.setMessage("头条更新了新闻"); } }
运行结果
I am leesf, received updated message from subject is [leesf在博客园上发布了一篇新文章] ---------------------------------------- I am leesf, received updated message from subject is [leesf在CSDN上发布了一篇新文章] I am dyd, received updated message from subject is [leesf在CSDN上发布了一篇新文章] ---------------------------------------- I am dyd, received updated message from subject is [头条更新了新闻] I am ld, received updated message from subject is [头条更新了新闻]
说明:可以看到,不同的主题更新,会通知向该主题注册的观察者。并且新增的主题和观察者并没有修改原来的Subject、Observer代码,使得其具有很好的可维护性。
有了观察者的概念,再看JDK里面的观察者模式就会非常简单,有兴趣的读者可以自行查阅。笔者不再累赘。
四、总结
掌握了观察者模式后,在进行系统设计的时候,若可以使用观察者模式,则尽管去使用观察者模式吧,它会让你的系统更加灵活和易于扩展。所有源代码已经上传至github,欢迎fork,谢谢各位园友的观看~