Observer:观察者模式
先代码
该文章代码主要分三个版本:观察者原理实现版本、基于原理实现改进版本、Java实现版本。接下来一次做代码展示:
原理基本实现版本:
package h.l.demo.observer.explain;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author: Is-Me-Hl
* @date: 2020年2月25日
* @Description: 主题类/抽象通知者、发布者
*/
public class Subject {
// 订阅者集合
private List<Observer> observes = new ArrayList<>();
// 添加订阅者
public void addObserve(Observer observer) {
observes.add(observer);
}
// 移除订阅者
public void removeObserver(Observer observer) {
observes.remove(observer);
}
// 通知所有的订阅者,即发布消息
public void notifyObservers(Object arg) {
for (Observer observer : observes) {
observer.update(this,arg);
}
}
}
package h.l.demo.observer.explain;
/**
*
* @author: Is-Me-Hl
* @date: 2020年2月25日
* @Description: 抽象观察者
*/
public abstract class Observer {
public abstract void update(Subject subject, Object arg);
}
class Observer1 extends Observer {
@Override
public void update(Subject subject, Object arg) {
System.out.println(this.getClass().getName()+":发布者:" + subject + ";发布消息:" + arg);
}
}
class Observer2 extends Observer {
@Override
public void update(Subject subject, Object arg) {
System.out.println(this.getClass().getName()+":发布者:" + subject + ";发布消息:" + arg);
}
}
测试:
package h.l.demo.observer.explain;
/**
*
* @author: Is-Me-Hl
* @date: 2020年1月31日
* @Description: 测试
*/
public class TestMainEnter {
public static void main(String[] args) {
Subject subject = new Subject();
subject.addObserve(new Observer1());
subject.addObserve(new Observer2());
subject.notifyObservers(null);
System.out.println("-----------------");
subject.notifyObservers("Is-Me-HL,Fighting!!!");
}
}
测试结果:
基于原理实现的改进版:使用反射+事件委托,解除在抽象类中发布者和订阅者之间的耦合:
package h.l.demo.observer.explain_improved_version;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Observer {
// 订阅者/观察者
private Object object;
// 订阅者/观察者 被调用的方法名
private String methodName;
// 订阅者/观察者方法调用的参数
private Object[] args;
// 方法调用传入参数,其每个参数对应的数据类型
private Class<?>[] argsTypes;
public Observer(Object object, String methodName, Object... args) {
this.object = object;
this.methodName = methodName;
this.args = args;
if (this.args != null) {
getArgsTypes(this.args);
}
}
private void getArgsTypes(Object[] args) {
this.argsTypes = new Class[args.length + 1];
this.argsTypes[0] = new Object().getClass();
for (int i = 1; i <= args.length; i++) {
this.argsTypes[i] = args[i - 1].getClass();
}
}
public void invoke(Object publishContext) throws NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
// 判断方法是否存在
Method method = this.object.getClass().getMethod(this.methodName,
this.argsTypes);
// 执行方法
Object[] newObjectArr = new Object[this.args.length + 1];
newObjectArr[0] = publishContext;
for (int i = 1; i <= this.args.length; i++) {
newObjectArr[i] = this.args[i - 1];
}
method.invoke(this.object, newObjectArr);
}
}
class Observer1 {
public void observer1Update(Object publishContext, String arg) {
System.out.println("SubjectContext:" + publishContext + ";"
+ this.getClass().getName() + ";发布消息:" + arg);
}
}
class Observer2 {
public void observer2Update(Object publishContext, String arg) {
System.out.println("SubjectContext:" + publishContext + ";"
+ this.getClass().getName() + ";发布消息:" + arg);
}
}
这里要注意的是Observer类实际上就不在是观察者/订阅者类了,只是直接延用了这个类名,但改变了他的性质,实际上这个类目前的作用就是通过对客户端传进来的参数,实例化出具体的观察者,在指定方法中通过invoke方法代替具体观察类执行指定的方法。
package h.l.demo.observer.explain_improved_version;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author: Is-Me-Hl
* @date: 2020年2月25日
* @Description: 主题类/抽象通知者、发布者
*/
public class Subject {
// 订阅者集合
private List<Observer> observes = new ArrayList<>();
// 添加订阅者
public void addObserve(Observer observer) {
observes.add(observer);
}
// 移除订阅者
public void removeObserver(Observer observer) {
observes.remove(observer);
}
// 通知所有的订阅者,即发布消息
public void notifyObservers(Object arg) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (Observer observer : observes) {
observer.invoke(arg);
}
}
}
测试:
package h.l.demo.observer.explain_improved_version;
import java.lang.reflect.InvocationTargetException;
/**
*
* @author: Is-Me-Hl
* @date: 2020年1月31日
* @Description: 测试
*/
public class TestMainEnter {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Subject subject = new Subject();
subject.addObserve(new Observer(new Observer1(), "observer1Update", "hello,observer1Update"));
subject.addObserve(new Observer(new Observer2(), "observer2Update", "hello,observer2Update"));
subject.notifyObservers("I am subject,是我给你们发布的通知");
System.out.println("---------------");
subject.notifyObservers(null);
System.out.println("---------------");
Subject subject2 = new Subject();
subject2.addObserve(new Observer(new Observer1(), "observer1Update", ""));
subject2.addObserve(new Observer(new Observer2(), "observer2Update", ""));
subject2.notifyObservers(null);
}
}
测试结果:
观察者模式Java实现版本:
package h.l.demo.observer.javaimpl;
import java.util.Observable;
/**
*
* @author: Is-Me-Hl
* @date: 2020年2月25日
* @Description: 被观察者/发布者
*/
public class MyObservable extends Observable {
public static void main(String[] args) {
MyObservable myObservable = new MyObservable();
myObservable.addObserver(new MyObserve1());
myObservable.addObserver(new MyObserve2());
// 发布消息
myObservable.setChanged();
myObservable.notifyObservers();
// 发布带参数的消息
myObservable.setChanged();
myObservable.notifyObservers("我是发布者,我现在通知你们");
}
}
package h.l.demo.observer.javaimpl;
import java.util.Observable;
import java.util.Observer;
/**
*
* @author: Is-Me-Hl
* @date: 2020年2月25日
* @Description: 观察者模式也称为“发布订阅模式”。观察者类1:相当于“发布-订阅”说法中的订阅者
*/
public class MyObserve1 implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("观察对象(订阅对象)" + this.getClass().getName() + ";");
System.out.println("被观察对象(发布对象为):" + o + ",传递的参数为:" + arg);
}
}
package h.l.demo.observer.javaimpl;
import java.util.Observable;
import java.util.Observer;
/**
*
* @author: Is-Me-Hl
* @date: 2020年2月25日
* @Description: 观察者模式也称为“发布订阅模式”。观察者类2:相当于“发布-订阅”说法中的订阅者
*/
public class MyObserve2 implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("观察对象(订阅对象)" + this.getClass().getName() + ";");
System.out.println("被观察对象(发布对象为):" + o + ",传递的参数为:" + arg);
}
}
实际上,跟踪Observable 类的代码,会发现和上面第一种原理基本实现的方法是一样的。Java给我们提供了这样的方法。观察者和被观察者类的耦合度是有的,如果要改进,可以使用委托的形式。
测试结果:
后分析
- 个人建议:写代码是件幸福的事,So,do it
观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生改变时,会通知所有观察者对象,使他们能自动更新自己。什么时候用观察者模式呢?当一个对象的改变需要同时改变其他对象的时候,并且其他对象的数据情况不知道的情况下。其实观察者模式在生活中有很多的例子:比如说录音机,你收听了哪个频道,一旦频道有内容发布了,你就能听到。比如天气预报,一旦天气改变了就会将这个变更通知到大家,大家多穿衣服或者少穿衣服,等等。总结就是,N个观察者订阅,1个被观察者发布,当然这里的观察者和被观察者是指的角色,角色关系是N:1,事实上这个被观察者的1是个抽象,具体通知观察者的人或许是X,亦或许是Y。
其他例子:参考《大话设计模式》老板回来,前台负责通知员工关闭股票、关闭NBA
注:以上文章仅是个人总结,若有不当之处,望不吝赐教