觀察者模式,是使用不少的一種模式,初次瞭解,只是根據入門demo寫個例子,可是不知道用在哪,怎麼用,很教科書。
我的很喜歡比較實戰的的博客或者資料。
最近又餓補了一把,感受有點小收穫,記錄下。html
Observerjava
/** * 觀察者接口,定義一個更新的接口給那些在目標發生改變的時候被通知的對象 */ public interface Observer { /** * 更新的接口 */ public void update(Subject subject); }
ConcreteObserverspring
//具體觀察者對象,實現更新的方法,使用自身的狀態和 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); } } }
ConcreteSubjectapi
/** * 具體的目標對象,負責吧有關狀態存入到相應的觀察者對象 * 並在本身狀態 */ 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:已經下發了"); } }
運行結果: 主題發一個消息,觀察者都能收到:
安全
Subjectspringboot
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); } } }
Observerapp
/** * 觀察者接口 * @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個階段:
準備階段,維護目標和觀察者關係的階段
實際運行階段,也就是目標發生變化,引發觀察者作出發應的階段
這裏說的使用方式,針對的是實際運行階段。獲取目標確切數據發起者的問題。
開發中若是數據肯定,能夠用推模型,若是觀察者要獲得的數據不固定,建議用拉模型,更加靈活,擴展性強。總之,仍是拉模型好。
新手通常只學到上面。下面進入入深刻思考的部分
:
上面的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,好好體會學習,之後借鑑使用下。
上面的例子,都是有個抽象觀察者的角色的,目標對象直接操做抽象觀察者。若是不想使用抽象觀察者,考慮的思路以下:
使用java自帶的觀察者模式
,他已經幫咱們自動提供了抽象觀察者,和抽象目標類,咱們按照他的規則直接使用就能夠了。反射委託
。這裏一會展現4個例子:
這個例子很特別,觀察者能夠觀察多個目標對象
)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); } }
運行結果:
查看結果,完美的展現了目標對象通知全部觀察者的實現。
具體代碼:
/** * 事件對象的封裝類 * * @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組件監聽的例子就簡單多了,由於原理徹底同樣,都是有事件和事件處理者管理和調用觀察者的方法。
具體看下代碼:
這個例子比較強大,既體現了委託註冊觀察者,又有某個觀察者的方法對應某個目標類的具體方法,方法到方法的對應。好好理解,學習,爭取項目中使用下:
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(){ } }
運行結果:
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(); } }
運行結果:
顯示,事件發佈後,監聽這就能觀察到這個事件了,能夠作更多的操做。
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
http://www.javashuo.com/article/p-fstdghwy-hv.html
我的微信公衆號:
搜索: 盛開de每一天
不定時推送相關文章,期待和你們一塊兒成長!!
完
謝謝你們支持!