《Design Patterns》Observer.積跬步系列

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


注:以上文章僅是個人總結,若有不當之處,望不吝賜教

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章