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
注:以上文章僅是個人總結,若有不當之處,望不吝賜教