觀察者模式,是使用很多的一種模式,初次瞭解,只是根據入門demo寫個例子,但是不知道用在哪,怎麼用,很教科書。 個人很喜歡比較實戰的的博客或者資料。 最近又餓補了一把,感覺有點小收穫,記錄下。 基礎部分 一、觀察者模式的基本經典結構 二、入門小demo 經典小demo1: Observer /** ...
觀察者模式,是使用很多的一種模式,初次瞭解,只是根據入門demo寫個例子,但是不知道用在哪,怎麼用,很教科書。
個人很喜歡比較實戰的的博客或者資料。
最近又餓補了一把,感覺有點小收穫,記錄下。
基礎部分
一、觀察者模式的基本經典結構
二、入門小demo
經典小demo1:
Observer
/**
* 觀察者介面,定義一個更新的介面給那些在目標發生改變的時候被通知的對象
*/
public interface Observer {
/**
* 更新的介面
*/
public void update(Subject subject);
}
ConcreteObserver
//具體觀察者對象,實現更新的方法,使用自身的狀態和
public class ConcreteObserver implements Observer {
@Override
public void update(Subject subject) {
//具體的實現
//這裡可能需要更新觀察者的狀態,使其與目標的狀態保持一致
String message = ((ConcreteSubject) subject).getSubjectState();
System.out.println("收到一通知: 獲取到的狀態是: " + message);
}
}
Subject
/**
* 目標對象,它知道觀察它的觀察者,並提供註冊和刪除觀察者的介面
*/
public class Subject {
/**
* 用來保存註冊的觀察者對象
*/
private List<Observer> observers = new ArrayList<>();
/**
* 註冊觀察者對象
*/
public void attach(Observer observer){
observers.add(observer);
}
/**
* 刪除觀察者對象
*/
public void detach(Observer observer){
observers.remove(observer);
}
/**
* 通知所有註冊的觀察者對象
*/
protected void notifyObservers(){
for (Observer observer: observers){
observer.update(this);
}
}
}
ConcreteSubject
/**
* 具體的目標對象,負責吧有關狀態存入到相應的觀察者對象
* 併在自己狀態
*/
public class ConcreteSubject extends Subject {
/**
* 目標對象的狀態
*/
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
this.notifyObservers();
}
}
Test
public class Test {
public static void main(String[] args) {
//觀察者
Observer concreteObserver1 = new ConcreteObserver();
Observer concreteObserver2 = new ConcreteObserver();
//目標對象 即被觀察者 目標可以有多個,此demo通過 state區分
ConcreteSubject subject1 = new ConcreteSubject();
//註冊觀察者
subject1.attach(concreteObserver1);
subject1.attach(concreteObserver2);
// ConcreteSubject subject2 = new ConcreteSubject();
// //註冊觀察者
// subject2.attach(concreteObserver1);
// subject2.attach(concreteObserver2);
//發出通知
subject1.setSubjectState("通知1:已經下發了");
// System.out.println("===換一個主題======");
// subject2.setSubjectState("通知2:已經下發了");
}
}
運行結果: 主題發一個消息,觀察者都能收到:
大話設計模式中看門放哨小案例
Subject
public interface Subject {
/**
* 添加觀察者
* @param observer
*/
void addObserver(Observer observer);
/**
* 移除指定的觀察者
* @param observer
*/
void removeObserver(Observer observer);
/**
* 移除所有的觀察者
*/
void removeAll();
/**
* data 是要通知給觀察者的數據
* 因為Object是所有類的父類,可以使用多態,當然 你也可以使用 泛型
* @param data
*/
void notifyAllObserver(Object data);
/**
* 單獨 通知某一個觀察者
* @param observer
* @param data
* data 是要通知給觀察者的數據
* 因為Object是所有類的父類,可以使用多態,當然 你也可以使用 泛型
*/
void notify(Observer observer,Object data);
}
ConcreteSubject
/**
* 具體的主題對象
* 這裡就不實現線程安全的功能了,
* 有興趣的話可以參考java.util報下的Observable
* @author xujun
*
*/
public class ConcreteSubject implements Subject {
List<Observer> mList = new ArrayList<>();
@Override
public void addObserver(Observer observer) {
// 確保相同的觀察者只含有一個
if (observer == null) {
throw new NullPointerException("observer == null");
}
if (!mList.contains(observer)) {
mList.add(observer);
}
}
@Override
public void removeObserver(Observer observer) {
mList.remove(observer);
}
@Override
public void removeAll() {
mList.clear();
}
@Override
public void notifyAllObserver(Object data) {
for (Observer observer : mList) {
observer.update(data);
}
}
@Override
public void notify(Observer observer, Object data) {
if(observer!=null){
observer.update(data);
}
}
}
Observer
/**
* 觀察者介面
* @author Administrator
*
*/
public interface Observer {
/**
*
* @param data 被觀察者傳遞給觀察者的 數據
*/
void update(Object data);
}
CartoonObserver
public class CartoonObserver implements Observer {
@Override
public void update(Object data) {
System.out.println( " 我是"+this.getClass().
getSimpleName()+", "+data+"別看漫畫了");
}
}
NBAObserver
public class NBAObserver implements Observer {
public class CartoonObserver implements Observer {
@Override
public void update(Object data) {
System.out.println( " 我是"+this.getClass().getSimpleName()+", "+data+"別看漫畫了");
}
}
@Override
public void update(Object data) {
System.out.println(" 我是" + this.getClass().getSimpleName() + ", " + data + "別看NBA了");
}
}
TestObserver
public class TestObserver {
public static void main(String[] args) {
//主題
ConcreteSubject concreteSubject = new ConcreteSubject();
//觀察者
CartoonObserver cartoonObserver = new CartoonObserver();
NBAObserver nbaObserver = new NBAObserver();
//添加觀察者
concreteSubject.addObserver(cartoonObserver);
concreteSubject.addObserver(nbaObserver);
//發佈消息通知
concreteSubject.notifyAllObserver("老師來了");
}
}
運行結果: 只要放哨的一發通知,觀察者就收到了。
三、經典觀察者模式的兩種使用方式: 推和拉
觀察者模式使用時,其實分2個階段:
-
準備階段,維護目標和觀察者關係的階段
-
實際運行階段,也就是目標發生變化,引起觀察者做出發應的階段
這裡說的使用方式,針對的是實際運行階段。獲取目標確切數據發起者的問題。
- 推模型
目標對象主動向觀察者推送目標的詳細信息,不管觀察者是否需要,這些數據,是目標對象定義,相當於廣播給觀察者。剛纔的例子中,第2個就是推模型。 - 拉模型
目標對象在通知觀察者的時候,只是把自己的引用給觀察者,觀察者根據需要,使用引用獲取。剛纔的例子中,第一個例子就是拉模型。目標對象吧this傳遞給觀察者。
開發中如果數據確定,可以用推模型,如果觀察者要得到的數據不固定,建議用拉模型,更加靈活,擴展性強。總之,還是拉模型好。
新手一般只學到上面。下麵進入入深入思考的部分
:
高級部分(應用場景)
一、 如何讓觀察者區別對待
上面的demo,均是目前通知觀察者的時候全部都通知,根據不同的情況來讓不同的觀察者處理操作,如何設計呢?
思路
:2種,
一種是目標可以全部通知,但是觀察者不做任何操作,
另一種就是在目標裡面進行判斷,直接不通知了,
這裡推薦第二種,可以統一邏輯控制,併進行觀察者的統一分派,有利於業務控制和今後的擴展。
來個例子: 水質污染,根據污染情況分別通知檢測員,預警人員,檢測部門領導。
代如下:
WaterQualitySubject - 水質監測的目標對象
/**
* 定義水質監測的目標對象
*/
public abstract class WaterQualitySubject {
/**
* 用來保存註冊
*/
protected List<WatcherObserver> observers = new ArrayList<>();
/**
* 註冊觀察者對象
*/
public void attach(WatcherObserver observer){
observers.add(observer);
}
/**
* 刪除觀察者對象
*/
public void detach(WatcherObserver observer){
observers.remove(observer);
}
/**
* 通知相應的觀察者對象
*/
public abstract void notifyWathers();
/**
* 獲取水質污染的級別
*/
public abstract int getPolluteLevel();
}
WaterQuality - 具體的水質監測對象
/**
* 具體的水質監測對象
*/
public class WaterQuality extends WaterQualitySubject {
/**
* 污染的級別,0表示正常,1表示輕度污染,2表示中度污染,3表示高度污染
*/
private int polluteLevel = 0;
/**
* 獲取水質污染的級別
*/
@Override
public int getPolluteLevel() {
return polluteLevel;
}
public void setPolluteLevel(int polluteLevel) {
this.polluteLevel = polluteLevel;
this.notifyWathers();
}
/**
* 通知相應的觀察者對象
*/
@Override
public void notifyWathers() {
//迴圈所在註冊的觀察者
for (WatcherObserver watcher: observers){
//開始根據污染級別判斷是否需要通知,由這裡總控
if (this.polluteLevel >=0 ){
//通知監測員做記錄
if (("監測人員").equals(watcher.getJob())){
watcher.update(this);
}
}
if (this.polluteLevel >=1 ){
//通知預警人員
if (("預警人員").equals(watcher.getJob())){
watcher.update(this);
}
}
if (this.polluteLevel >=2 ){
//通知監測員部門領導
if (("監測部門領導").equals(watcher.getJob())){
watcher.update(this);
}
}
}
}
}
WatcherObserver
public interface WatcherObserver {
/**
* 被通知的方法
* @param subject
*/
public void update(WaterQualitySubject subject);
/**
* 設置觀察人員的職務
*/
public void setJob(String job);
/**
* 獲取觀察人員的職務
*/
public String getJob();
}
Watcher
public class Watcher implements WatcherObserver{
private String job;
@Override
public void update(WaterQualitySubject subject) {
//這裡採用的是拉的方式
System.out.println(job+"獲取到通知,當前污染級別為:" + subject.getPolluteLevel());
}
@Override
public void setJob(String job) {
this.job = job;
}
@Override
public String getJob() {
return this.job;
}
}
Test
public class Test {
public static void main(String[] args) {
//創建水質主題對象
WaterQuality subject = new WaterQuality();
//創建幾個觀察者
WatcherObserver watcher1 = new Watcher();
watcher1.setJob("監測人員");
WatcherObserver watcher2 = new Watcher();
watcher2.setJob("預警人員");
WatcherObserver watcher3 = new Watcher();
watcher3.setJob("監測部門領導");
//註冊觀察者
subject.attach(watcher1);
subject.attach(watcher2);
subject.attach(watcher3);
//填寫水質報告
System.out.println("當水質為正常的時候-----------");
subject.setPolluteLevel(0);
System.out.println("當水質為輕度污染的時候-----------");
subject.setPolluteLevel(1);
System.out.println("當水質為中度污染的時候-----------");
subject.setPolluteLevel(2);
}
}
運行結果:
可以看到根據預警級別通知了不同的觀察者。
主要邏輯在目標對象實現類上,在if的判斷上是比較取巧的寫法,不是if 。。else if .. .,而是多個if。組合if,好好體會學習,以後借鑒使用下。
二、如何不依賴抽象觀察者,也能實現觀察者模式:
上面的例子,都是有個抽象觀察者的角色的,目標對象直接操作抽象觀察者。如果不想使用抽象觀察者,考慮的思路如下:
- 1.我們新建抽象觀察者類,別人給提供,我們實現就好了,這裡我們可以
使用java自帶的觀察者模式
,他已經幫我們自動提供了抽象觀察者,和抽象目標類,我們按照他的規則直接使用就可以了。 - [ ] 這裡一會展示 使用java自帶的觀察者模式的例子:
- 2.註冊觀察者和通知觀察者的工作交給一個第三方,解耦目標和觀察者。
這種實現的方式很多,主要思路是反射委托
。
這裡一會展示4個例子:
- [ ] 反射委托實現上面第2個例子,上課看nba和動漫,老師來了,做出不同反應的例子。
- [ ] 比如:模仿swing組件實現原理的例子(
這個例子很特別,觀察者可以觀察多個目標對象
) - [ ] spring中listener實現觀察者模式的例子
- [ ] springboot使用Guava框架提供的eventBus,實現事件處理的例子。
1.使用java自帶的觀察者模式的例子
java提供抽象觀察者Observer,抽象目標對象Observable,通知觀察者的方法名必須是update。通知前必須調動setChange()方法,具體代碼如下:
具體目標類(被觀察者)
/**
* Title: GPer
* Description: JDK提供的一種觀察者的實現方式,被觀察者
*
* @author hfl
* @version V1.0
* @date 2020-06-03
*/
public class GPer extends Observable {
private String name = "GPer生態圈";
private static GPer gper = null;
private GPer() {
}
public static GPer getInstance(){
if(null == gper){
gper = new GPer();
}
return gper;
}
public void publishQuestion(Question question){
System.out.println(question.getUserName() + "在" + this.name + "上提交了一個問題。");
setChanged();
notifyObservers(question);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
來個輔助的類,和觀察者模式沒啥關係的業務類:Question
/**
* Title: Question
* Description: TODO
*
* @author hfl
* @version V1.0
* @date 2020-06-03
*/
public class Question {
//提問者
private String userName;
//提問問題
private String content;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
具體的觀察者類:Teacher
public class Teacher implements Observer {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
GPer gper = (GPer)o;
Question question = (Question)arg;
System.out.println("===============================");
System.out.println(name + "老師,你好!\n" +
"您收到了一個來自“" + gper.getName() + "”的提問,希望您解答,問題內容如下:\n" +
question.getContent() + "\n" +
"提問者:" + question.getUserName());
}
}
測試類:
public class ObserverTest {
public static void main(String[] args) {
GPer gper = GPer.getInstance();
Teacher tom = new Teacher("Tom");
Teacher mic = new Teacher("Mic");
//這為沒有@Tom老師
Question question = new Question();
question.setUserName("小明");
question.setContent("觀察者設計模式適用於哪些場景?");
gper.addObserver(tom);
gper.addObserver(mic);
gper.publishQuestion(question);
}
}
運行結果:
查看結果,完美的展示了目標對象通知所有觀察者的實現。
2.反射委托實現上面第2個例子,學生根據老師到來,做出不同反應的例子。
具體代碼:
/**
* 事件對象的封裝類
*
* @author Administrator
*/
public class Event {
//要執行方法的對象
private Object object;
//要執行的方法名稱
private String methodName;
//要執行方法的參數
private Object[] params;
//要執行方法的參數類型
private Class[] paramTypes;
public Event() {
}
public Event(Object object, String methodName, Object... args) {
this.object = object;
this.methodName = methodName;
this.params = args;
contractParamTypes(this.params);
}
//根據參數數組生成參數類型數組
private void contractParamTypes(Object[] params) {
this.paramTypes = new Class[params.length];
for (int i = 0; i < params.length; i++) {
this.paramTypes[i] = params[i].getClass();
}
}
public Object getObject() {
return object;
}
//這裡省略了若幹get和set方法
/**
* 根據該對象的方法名,方法參數,利用反射機制,執行該方法
*
* @throws Exception
*/
public void invoke() throws Exception {
Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
if (null == method) {
return;
}
method.invoke(this.getObject(), this.getParams());
}
public void setObject(Object object) {
this.object = object;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
public Class[] getParamTypes() {
return paramTypes;
}
public void setParamTypes(Class[] paramTypes) {
this.paramTypes = paramTypes;
}
}
EventHandler
/**
* Title: EventHandler
* Description: 事件的 處理者
*
* @author hfl
* @version V1.0
* @date 2020-06-05
*/
public class EventHandler {
//是用一個List
private List<Event> objects;
public EventHandler(){
objects=new ArrayList<Event>();
}
//添加某個對象要執行的事件,及需要的參數
public void addEvent(Object object,String methodName,Object...args){
objects.add(new Event(object,methodName,args));
}
//通知所有的對象執行指定的事件
public void notifyX() throws Exception{
for(Event e : objects){
e.invoke();
}
}
}
通知者的 抽象類Notifier
public abstract class Notifier {
private EventHandler eventHandler = new EventHandler();
public EventHandler getEventHandler() {
return eventHandler;
}
public void setEventHandler(EventHandler eventHandler) {
this.eventHandler = eventHandler;
}
/**
* 增加需要幫忙 放哨 的 學生
*
* @param object 要執行方法的對象
* @param methodName 執行方法 的方法名
* @param args 執行方法的參數
*/
public abstract void addListener(Object object, String methodName, Object... args);
/**
* 告訴所有要幫忙放哨的學生:老師來了
*/
public abstract void notifyX();
}
通知者 GoodNotifier
public class GoodNotifier extends Notifier {
@Override
public void addListener(Object object, String methodName, Object... args) {
System.out.println("有新的同學委托盡職盡責的放哨人!");
EventHandler handler = this.getEventHandler();
handler.addEvent(object, methodName, args);
}
@Override
public void notifyX() {
System.out.println("盡職盡責的放哨人告訴所有需要幫忙的同學:老師來了");
try{
this.getEventHandler().notifyX();
}catch(Exception e){
e.printStackTrace();
}
}
WatchCartoonListener
/**
* Title: WatchCartoonListener
* Description: 具體監聽者(觀察者)
*
* @author hfl
* @version V1.0
* @date 2020-06-05
*/
public class WatchCartoonListener extends GoodNotifier {
public WatchCartoonListener() {
System.out.println("WatchCartoonListener 我正在看漫畫,開始時間:"+ LocalDateTime.now().toString());
}
public void stopPlayingGame(Date date){
System.out.println("WatchCartoonListener 停止看漫畫了,結束時間:"+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));
}
}
WatchingNBAListener
public class WatchingNBAListener extends GoodNotifier {
public WatchingNBAListener() {
System.out.println("WatchingNBAListener我正在看NBA,開始時間是: " + LocalDateTime.now().toString());
}
public void stopWatchingTV(Date date){
System.out.println("WatchingNBAListener 快關閉NBA直播 , 結束時間是:" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));
}
}
測試類:
public class Test {
public static void main(String[] args) {
//創建一個盡職盡責的放哨者
Notifier goodNotifier = new GoodNotifier();
//創建一個玩游戲的同學,開始玩游戲
WatchCartoonListener playingGameListener = new WatchCartoonListener();
//創建一個看電視的同學,開始看電視
WatchingNBAListener watchingTVListener = new WatchingNBAListener();
//玩游戲的同學告訴放哨的同學,老師來了告訴一下
goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
//看電視的同學告訴放哨的同學,老師來了告訴一下
goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
try {
//一點時間後
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
//老師出現,放哨的人通知所有要幫忙的同學:老師來了
goodNotifier.notifyX();
}
}
運行結果:
public class Test {
public static void main(String[] args) {
//創建一個盡職盡責的放哨者
Notifier goodNotifier = new GoodNotifier();
//創建一個玩游戲的同學,開始玩游戲
WatchCartoonListener playingGameListener = new WatchCartoonListener();
//創建一個看電視的同學,開始看電視
WatchingNBAListener watchingTVListener = new WatchingNBAListener();
//玩游戲的同學告訴放哨的同學,老師來了告訴一下
goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
//看電視的同學告訴放哨的同學,老師來了告訴一下
goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
try {
//一點時間後
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
//老師出現,放哨的人通知所有要幫忙的同學:老師來了
goodNotifier.notifyX();
}
}
運行結果:
事件委托機制 分析
1.放哨者完全不知道做游戲者的存在,完全解耦。(當然,功勞歸功於Event和EventHandler,且這兩個類具有通用性)
2.老師來了後游戲者停止游戲回到座位,看NBA者停止看NBA,看漫畫這停止看漫畫,玩游戲這停止玩游戲。(一次通知,執行了不同類的不同方法)
3.擴展性很高,再來一個打籃球的學生就先寫個打籃球學生類,併在測試代碼中告訴放哨者一下就好,放哨者完全沒有變。重用性好
看了這個例子,再來瞭解swing組件監聽的例子就簡單多了,因為原理完全一樣,都是有事件和事件處理者管理和調用觀察者的方法。
3.swing組件監聽的例子
具體看下代碼:
這個例子比較強大,既體現了委托註冊觀察者,又有某個觀察者的方法對應某個目標類的具體方法,方法到方法的對應。好好理解,學習,爭取項目中使用下:
Event
public class Event {
//事件源,事件是由誰發起的保存起來
private Object source;
//事件觸發,要通知誰
private Object target;
//事件觸發,要做什麼動作,回調
private Method callback;
//事件的名稱,觸發的是什麼事件
private String trigger;
//事件觸發的時間
private long time;
public Event(Object target, Method callback) {
this.target = target;
this.callback = callback;
}
public Event setSource(Object source) {
this.source = source;
return this;
}
public Event setTime(long time) {
this.time = time;
return this;
}
public Event setTrigger(String trigger) {
this.trigger = trigger;
return this;
}
public Object getSource() {
return source;
}
public long getTime() {
return time;
}
public Object getTarget() {
return target;
}
public Method getCallback() {
return callback;
}
@Override
public String toString() {
return "Event{" + "\n" +
"\tsource=" + source.getClass() + ",\n" +
"\ttarget=" + target.getClass() + ",\n" +
"\tcallback=" + callback + ",\n" +
"\ttrigger='" + trigger + "',\n" +
"\ttime=" + time + "'\n" +
'}';
}
}
EventLisenter
public class EventLisenter {
//JDK底層的Lisenter通常也是這樣來設計的
protected Map<String, Event> events = new HashMap<String, Event>();
//事件名稱和一個目標對象來觸發事件
public void addLisenter(String eventType, Object target) {
try {
this.addLisenter(
eventType,
target,
target.getClass().getMethod("on" + toUpperFirstCase(eventType), Event.class));
} catch (Exception e) {
e.printStackTrace();
}
}
public void addLisenter(String eventType, Object target, Method callback) {
//註冊事件
events.put(eventType, new Event(target, callback));
}
//觸發,只要有動作就觸發
private void trigger(Event event) {
event.setSource(this);
event.setTime(System.currentTimeMillis());
try {
//發起回調
if (event.getCallback() != null) {
//用反射調用它的回調函數
event.getCallback().invoke(event.getTarget(), event);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//事件名稱觸發
protected void trigger(String trigger) {
if (!this.events.containsKey(trigger)) {
return;
}
trigger(this.events.get(trigger).setTrigger(trigger));
}
//邏輯處理的私有方法,首字母大寫
private String toUpperFirstCase(String str) {
char[] chars = str.toCharArray();
chars[0] -= 32;
return String.valueOf(chars);
}
}
Mouse
public class Mouse extends EventLisenter {
public void click(){
System.out.println("調用單擊方法");
this.trigger(MouseEventType.ON_CLICK);
}
public void doubleClick(){
System.out.println("調用雙擊方法");
this.trigger(MouseEventType.ON_DOUBLE_CLICK);
}
public void up(){
System.out.println("調用彈起方法");
this.trigger(MouseEventType.ON_UP);
}
public void down(){
System.out.println("調用按下方法");
this.trigger(MouseEventType.ON_DOWN);
}
public void move(){
System.out.println("調用移動方法");
this.trigger(MouseEventType.ON_MOVE);
}
public void wheel(){
System.out.println("調用滾動方法");
this.trigger(MouseEventType.ON_WHEEL);
}
public void over(){
System.out.println("調用懸停方法");
this.trigger(MouseEventType.ON_OVER);
}
public void blur(){
System.out.println("調用獲焦方法");
this.trigger(MouseEventType.ON_BLUR);
}
public void focus(){
System.out.println("調用失焦方法");
this.trigger(MouseEventType.ON_FOCUS);
}
}
MouseEventCallback
/**
* 自己寫的邏輯,用於回調
* Created by Tom.
*/
public class MouseEventCallback {
public void onClick(Event e){
System.out.println("===========觸發滑鼠單擊事件==========" + "\n" + e);
}
public void onDoubleClick(Event e){
System.out.println("===========觸發滑鼠雙擊事件==========" + "\n" + e);
}
public void onUp(Event e){
System.out.println("===========觸發滑鼠彈起事件==========" + "\n" + e);
}
public void onDown(Event e){
System.out.println("===========觸發滑鼠按下事件==========" + "\n" + e);
}
public void onMove(Event e){
System.out.println("===========觸發滑鼠移動事件==========" + "\n" + e);
}
public void onWheel(Event e){
System.out.println("===========觸發滑鼠滾動事件==========" + "\n" + e);
}
public void onOver(Event e){
System.out.println("===========觸發滑鼠懸停事件==========" + "\n" + e);
}
public void onBlur(Event e){
System.out.println("===========觸發滑鼠失焦事件==========" + "\n" + e);
}
public void onFocus(Event e){
System.out.println("===========觸發滑鼠獲焦事件==========" + "\n" + e);
}
}
MouseEventType
public interface MouseEventType {
//單擊
String ON_CLICK = "click";
//雙擊
String ON_DOUBLE_CLICK = "doubleClick";
//彈起
String ON_UP = "up";
//按下
String ON_DOWN = "down";
//移動
String ON_MOVE = "move";
//滾動
String ON_WHEEL = "wheel";
//懸停
String ON_OVER = "over";
//失焦
String ON_BLUR = "blur";
//獲焦
String ON_FOCUS = "focus";
}
MouseEventTest
public class MouseEventTest {
public static void main(String[] args) {
MouseEventCallback callback = new MouseEventCallback();
Mouse mouse = new Mouse();
//@誰? @回調方法
mouse.addLisenter(MouseEventType.ON_CLICK,callback);
mouse.addLisenter(MouseEventType.ON_FOCUS,callback);
mouse.click();
mouse.focus();
}
}
public class Keybord extends EventLisenter {
public void down(){
}
public void up(){
}
}
運行結果:
4.spring中listener實現觀察者模式的例子
Spring的事件機制用到了觀察者模式。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。
在Spring中各個listener相當於觀察者。event事件相當於目標,觀察者,而容器用來管理和註冊觀察者,發佈事件。
具體的演示代碼如下:
事件類:OrderEvent 相當於目標類
public class OrderEvent extends ApplicationEvent {
public OrderEvent(Object source) {
super(source);
}
}
OrderSmsListener相當於觀察者
@Component
public class OrderSmsListener implements ApplicationListener<OrderEvent> {
@Override
public void onApplicationEvent(OrderEvent orderEvent) {
System.out.println("orderSmsListener receive event from " + orderEvent.getSource());
}
}
業務類:
@Service
public class OrderService {
@Autowired
private ApplicationContext applicationContext;
public void order() {
applicationContext.publishEvent(new OrderEvent("orderService"));
}
}
另外spring容器相當於委托類。
測試:
@RunWith(SpringRunner.class)
@SpringBootTest
public class PatternApplicationTest {
@Autowired
private OrderService orderService;
@Test
public void contextLoads() {
}
@Test
public void testOrder() {
orderService.order();
}
}
運行結果:
顯示,事件發佈後,監聽這就能觀察到這個事件了,可以做更多的操作。
4.springboot中集成谷歌的Guava,通過eventBus實現訂閱發佈功能
EventBus無需實現複雜的事件、監聽者、發佈者。前面講的是面向類,現在:Guava是面向是方法,更加強大。能夠輕鬆落地觀察模式的一種解決方案。
演示個例子:
引入Guava的包:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
EventBus 事件匯流排
//api封裝
public class MyEventBus {
/** 事件任務匯流排 */
private final static EventBus tiemEventBus = new EventBus();
/**
* 觸發同步事件
*
* @param event
*/
public static void post(Object event) {
tiemEventBus.post(event);
}
/**
* 註冊事件處理器
*
* @param handler
*/
public static void register(Object handler) {
tiemEventBus.register(handler);
}
/**
* 註銷事件處理器
*
* @param handler
*/
public static void unregister(Object handler) {
tiemEventBus.unregister(handler);
}
}
消息實體類
public class Message {
private MessageType messageType;
private String messageContent;
public Message(MessageType messageType, String messageContent) {
this.messageType = messageType;
this.messageContent = messageContent;
}
public MessageType getMessageType() {
return messageType;
}
public void setMessageType(MessageType messageType) {
this.messageType = messageType;
}
public String getMessageContent() {
return messageContent;
}
public void setMessageContent(String messageContent) {
this.messageContent = messageContent;
}
public enum MessageType {
OPENDOOR(1, "openDoor"),
CLOSEDOOR(2,"closeDoor");
private int code;
private String value;
MessageType(int code, String value) {
this.code = code;
this.value = value;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
事件監聽者
@Component
abstract class MyApplicationListener implements ApplicationListener<ApplicationPreparedEvent> {
/**
* ApplicationPreparedEvent 上下文準備事件
* @param applicationPreparedEvent
*/
@Override
public void onApplicationEvent(ApplicationPreparedEvent applicationPreparedEvent) {
ConfigurableApplicationContext applicationContext = applicationPreparedEvent.getApplicationContext();
MyApplicationListener bean = applicationContext.getBean(this.getClass());
System.out.println("regist listener to eventBus...."+bean);
MyEventBus.register(bean);
}
}
訂閱者(也即監聽者)繼承至MyApplicationListener。
@Component
public class MyListentenerSubscribe extends MyApplicationListener{
@Subscribe
public void on(Message message){
System.out.println("subscribe message-> messgeType:"+message.getMessageType()+"\n messageContent:"+message.getMessageContent());
}
}
測試:
@RestController
public class EventPublishCtrl extends LogBase {
@GetMapping("/publish")
public void publishEvent() {
log.info("this publish method...");
MyEventBus.post(new Message(Message.MessageType.OPENDOOR,"芝麻開門!"));
}
}
啟動項目,然後調用發佈方法:
訂閱者收到具體的消息類型,以及消息內容。
三、觀察者模式的本質
上面主要介紹了觀察者模式的基本經典結構;入門小demo;使用的2種方式;觀察者模式變形寫法;java中封裝好的觀察者模式使用方式;不依賴抽象觀察者的方式,比如使用反射委托例子,swing使用觀察者的例子,spring中listener觀察者模式實現的例子,springboot 的eventBus的觀察者模式的例子. 最後說下觀察者模式的本質。
自此,觀察者模式總算寫完了,其實後續可能會有加上mq的例子,還沒深入學習,先這樣吧。
參考文章:
https://blog.csdn.net/gdutxiaoxu/article/details/51824769
https://blog.csdn.net/fw19940314/article/details/100010397
https://www.cnblogs.com/wkzhao/p/10229283.html
個人微信公眾號:
搜索: 怒放de每一天
不定時推送相關文章,期待和大家一起成長!!
完
謝謝大家支持!