国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

使用Java8實現(xiàn)觀察者模式的方法(上)

2019-11-26 14:32:36
字體:
供稿:網(wǎng)友

觀察者(Observer)模式又名發(fā)布-訂閱(Publish/Subscribe)模式,是四人組(GoF,即 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides)在1994合著的《設(shè)計模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》中提出的(詳見書中293-313頁)。盡管這種模式已經(jīng)有相當長的歷史,它仍然廣泛適用于各種場景,甚至成為了標準Java庫的一個組成部分。目前雖然已經(jīng)有大量關(guān)于觀察者模式的文章,但它們都專注于在 Java 中的實現(xiàn),卻忽視了開發(fā)者在Java中使用觀察者模式時遇到的各種問題。

本文的寫作初衷就是為了填補這一空白:本文主要介紹通過使用 Java8 架構(gòu)實現(xiàn)觀察者模式,并在此基礎(chǔ)上進一步探討關(guān)于經(jīng)典模式的復(fù)雜問題,包括匿名內(nèi)部類、lambda 表達式、線程安全以及非平凡耗時長的觀察者實現(xiàn)。本文內(nèi)容雖然并不全面,很多這種模式所涉及的復(fù)雜問題,遠不是一篇文章就能說清的。但是讀完本文,讀者能了解什么是觀察者模式,它在Java中的通用性以及如何處理在 Java 中實現(xiàn)觀察者模式時的一些常見問題。

觀察者模式

根據(jù) GoF 提出的經(jīng)典定義,觀察者模式的主旨是:

定義對象間的一種一對多的依賴關(guān)系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。

什么意思呢?很多軟件應(yīng)用中,對象之間的狀態(tài)都是互相依賴的。例如,如果一個應(yīng)用專注于數(shù)值數(shù)據(jù)加工,這個數(shù)據(jù)也許會通過圖形用戶界面(GUI)的表格或圖表來展現(xiàn)或者兩者同時使用,也就是說,當?shù)讓訑?shù)據(jù)更新時,相應(yīng)的 GUI 組件也要更新。問題的關(guān)鍵在于如何做到底層數(shù)據(jù)更新時 GUI 組件也隨之更新,同時盡量減小 GUI 組件和底層數(shù)據(jù)的耦合度。

一種簡單且不可擴展的解決方案是給管理這些底層數(shù)據(jù)的對象該表格和圖像 GUI 組件的引用,使得對象可以在底層數(shù)據(jù)變化時能夠通知 GUI 組件。顯然,對于處理有更多 GUI 組件的復(fù)雜應(yīng)用,這個簡單的解決方案很快顯示出其不足。例如,有20個 GUI 組件都依賴于底層數(shù)據(jù),那么管理底層數(shù)據(jù)的對象就需要維護指向這20個組件的引用。隨著依賴于相關(guān)數(shù)據(jù)的對象數(shù)量的增加,數(shù)據(jù)管理和對象之間的耦合度也變得難以控制。

另一個更好的解決方案是允許對象注冊獲取感興趣數(shù)據(jù)更新的權(quán)限,當數(shù)據(jù)變化時,數(shù)據(jù)管理器就會通知這些對象。通俗地說就是,讓感興趣的數(shù)據(jù)對象告訴管理器:“當數(shù)據(jù)變化時請通知我”。此外,這些對象不僅可以注冊獲取更新通知,也可以取消注冊,保證數(shù)據(jù)管理器在數(shù)據(jù)變化時不再通知該對象。在 GoF 的原始定義中,注冊獲取更新的對象叫作“觀察者”(observer),對應(yīng)的數(shù)據(jù)管理器叫作“目標”(Subject),觀察者感興趣的數(shù)據(jù)叫作“目標狀態(tài)”,注冊過程叫“添加”(attach),撤銷觀察的過程叫“移除”(detach)。前文已經(jīng)提到觀察者模式又叫發(fā)布-訂閱模式,可以理解為客戶訂閱關(guān)于目標的觀察者,當目標狀態(tài)更新時,目標把這些更新發(fā)布給訂閱者(這種設(shè)計模式擴展為通用架構(gòu),稱為發(fā)布――訂閱架構(gòu))。這些概念可以用下面的類圖表示:

 

具體觀察者(ConcereteObserver)用來接收更新的狀態(tài)變化,同時將指向具體主題(ConcereteSubject)的引用傳遞給它的構(gòu)造函數(shù)。這為具體觀察者提供了指向具體主題的引用,在狀態(tài)變化時可由此獲得更新。簡單來說,具體觀察者會被告知主題更新,同時用其構(gòu)造函數(shù)中的引用來獲取具體主題的狀態(tài),最后將這些檢索狀態(tài)對象存儲在具體觀察者的觀察狀態(tài)(observerState)屬性下。這一過程如下面的序列圖所示:

 

經(jīng)典模式的專業(yè)化

盡管觀察者模式是通用的,但也有很多專業(yè)化的模式,最常見是以下兩種:

為State對象提供一個參數(shù),傳給觀察者調(diào)用的Update方法。在經(jīng)典模式下,當觀察者被通知Subject狀態(tài)發(fā)生變化后,會直接從Subject獲得其更新后狀態(tài)。這要求觀察者保存指向獲取狀態(tài)的對象引用。這樣就形成了一個循環(huán)引用,ConcreteSubject的引用指向其觀察者列表,ConcreteObserver的引用指向能獲得主題狀態(tài)的ConcreteSubject。除了獲得更新的狀態(tài),觀察者和其注冊監(jiān)聽的Subject間并沒有聯(lián)系,觀察者關(guān)心的是State對象,而非Subject本身。也就是說,很多情況下都將ConcreteObserver和ConcreteSubject強行聯(lián)系一起,相反,當ConcreteSubject調(diào)用Update函數(shù)時,將State對象傳遞給ConcreteObserver,二者就無需關(guān)聯(lián)。ConcreteObserver和State對象之間關(guān)聯(lián)減小了觀察者和State之間的依賴程度(關(guān)聯(lián)和依賴的更多區(qū)別請參見Martin Fowler's的文章)。

將Subject抽象類和ConcreteSubject合并到一個 singleSubject類中。多數(shù)情況下,Subject使用抽象類并不會提升程序的靈活性和可擴展性,因此,將這一抽象類和具體類合并簡化了設(shè)計。

這兩個專業(yè)化的模式組合后,其簡化類圖如下:

 

在這些專業(yè)化的模式中,靜態(tài)類結(jié)構(gòu)大大簡化,類之間的相互作用也得以簡化。此時的序列圖如下:

 

專業(yè)化模式另一特點是刪除了 ConcreteObserver 的成員變量 observerState。有時候具體觀察者并不需要保存Subject的最新狀態(tài),而只需要監(jiān)測狀態(tài)更新時 Subject 的狀態(tài)。例如,如果觀察者將成員變量的值更新到標準輸出上,就可以刪除 observerState,這樣一來就刪除了ConcreteObserver和State類之間的關(guān)聯(lián)。

更常見的命名規(guī)則

經(jīng)典模式甚至是前文提到的專業(yè)化模式都用的是attach,detach和observer等術(shù)語,而Java實現(xiàn)中很多都是用的不同的詞典,包括register,unregister,listener等。值得一提的是State是listener需要監(jiān)測變化的所有對象的統(tǒng)稱,狀態(tài)對象的具體名稱需要看觀察者模式用到的場景。例如,在listener監(jiān)聽事件發(fā)生場景下的觀察者模式,已注冊的listener將會在事件發(fā)生時收到通知,此時的狀態(tài)對象就是event,也就是事件是否發(fā)生。

平時實際應(yīng)用中目標的命名很少包含Subject。例如,創(chuàng)建一個關(guān)于動物園的應(yīng)用,注冊多個監(jiān)聽器用于觀察Zoo類,并在新動物進入動物園時收到通知。該案例中的目標是Zoo類,為了和所給問題域保持術(shù)語一致,將不會用到Subject這樣的詞匯,也就是說Zoo類不會命名為ZooSubject。

監(jiān)聽器的命名一般都會跟著Listener后綴,例如前文提到的監(jiān)測新動物加入的監(jiān)聽器會命名為AnimalAddedListener。類似的,register,、unregister和notify等函數(shù)命名常會以其對應(yīng)的監(jiān)聽器名作后綴,例如AnimalAddedListener的register、unregister、notify函數(shù)會被命名為registerAnimalAddedListener、 unregisterAnimalAddedListener和notifyAnimalAddedListeners,需要注意的是notify函數(shù)名的s,因為notify函數(shù)處理的是多個而非單一監(jiān)聽器。

這種命名方式會顯得冗長,而且通常一個subject會注冊多個類型的監(jiān)聽器,如前面提到的動物園的例子,Zoo內(nèi)除了注冊監(jiān)聽動物新增的監(jiān)聽器,還需注冊監(jiān)聽動物減少監(jiān)聽器,此時就會有兩種register函數(shù):(registerAnimalAddedListener和 registerAnimalRemovedListener,這種方式處理,監(jiān)聽器的類型作為一個限定符,表示其應(yīng)觀察者的類型。另一解決方案是創(chuàng)建一個registerListener函數(shù)然后重載,但是方案一能更方便的知道哪個監(jiān)聽器正在監(jiān)聽,重載是比較小眾的做法。

另一慣用語法是用on前綴而不是update,例如update函數(shù)命名為onAnimalAdded而不是updateAnimalAdded。這種情況在監(jiān)聽器獲得一個序列的通知時更常見,如向list中新增一個動物,但很少用于更新一個單獨的數(shù)據(jù),比如動物的名字。

接下來本文將使用Java的符號規(guī)則,雖然符號規(guī)則不會改變系統(tǒng)的真實設(shè)計和實現(xiàn),但是使用其他開發(fā)者都熟悉的術(shù)語是很重要的開發(fā)準則,因此要熟悉上文描述的Java中的觀察者模式符號規(guī)則。下文將在Java8環(huán)境下用一個簡單例子來闡述上述概念。

一個簡單的實例

還是前面提到的動物園的例子,使用Java8的API接口實現(xiàn)一個簡單的系統(tǒng),說明觀察者模式的基本原理。問題描述為:

創(chuàng)建一個系統(tǒng)zoo,允許用戶監(jiān)聽和撤銷監(jiān)聽添加新對象animal的狀態(tài),另外再創(chuàng)建一個具體監(jiān)聽器,負責輸出新增動物的name。

根據(jù)前面對觀察者模式的學(xué)習(xí)知道實現(xiàn)這樣的應(yīng)用需要創(chuàng)建4個類,具體是:

Zoo類:即模式中的主題,負責存儲動物園中的所有動物,并在新動物加入時通知所有已注冊的監(jiān)聽器。

Animal類:代表動物對象。

AnimalAddedListener類:即觀察者接口。

PrintNameAnimalAddedListener:具體的觀察者類,負責輸出新增動物的name。

首先我們創(chuàng)建一個Animal類,它是一個包含name成員變量、構(gòu)造函數(shù)、getter和setter方法的簡單Java對象,代碼如下:

public class Animal { private String name;public Animal (String name) {this.name = name;}public String getName () {return this.name;}public void setName (String name) {this.name = name;}}

用這個類代表動物對象,接下來就可以創(chuàng)建AnimalAddedListener接口了:

public interface AnimalAddedListener { public void onAnimalAdded (Animal animal);}

前面兩個類很簡單,就不再詳細介紹,接下來創(chuàng)建Zoo類:

public class Zoo { private List<Animal> animals = new ArrayList<>();private List<AnimalAddedListener> listeners = new ArrayList<>();public void addAnimal (Animal animal) {// Add the animal to the list of animalsthis.animals.add(animal);// Notify the list of registered listenersthis.notifyAnimalAddedListeners(animal);}public void registerAnimalAddedListener (AnimalAddedListener listener) {// Add the listener to the list of registered listenersthis.listeners.add(listener);}public void unregisterAnimalAddedListener (AnimalAddedListener listener) {// Remove the listener from the list of the registered listenersthis.listeners.remove(listener);}protected void notifyAnimalAddedListeners (Animal animal) {// Notify each of the listeners in the list of registered listenersthis.listeners.forEach(listener -> listener.updateAnimalAdded(animal));}}

這個類比前面兩個都復(fù)雜,其包含兩個list,一個用來存儲動物園中所有動物,另一個用來存儲所有的監(jiān)聽器,鑒于animals和listener集合存儲的對象都很簡單,本文選擇了ArrayList來存儲。存儲監(jiān)聽器的具體數(shù)據(jù)結(jié)構(gòu)要視問題而定,比如對于這里的動物園問題,如果監(jiān)聽器有優(yōu)先級,那就應(yīng)該選擇其他的數(shù)據(jù)結(jié)構(gòu),或者重寫監(jiān)聽器的register算法。

注冊和移除的實現(xiàn)都是簡單的委托方式:各個監(jiān)聽器作為參數(shù)從監(jiān)聽者的監(jiān)聽列表增加或者移除。notify函數(shù)的實現(xiàn)與觀察者模式的標準格式稍微偏離,它包括輸入?yún)?shù):新增加的animal,這樣一來notify函數(shù)就可以把新增加的animal引用傳遞給監(jiān)聽器了。用streams API的forEach函數(shù)遍歷監(jiān)聽器,對每個監(jiān)聽器執(zhí)行theonAnimalAdded函數(shù)。

在addAnimal函數(shù)中,新增的animal對象和監(jiān)聽器各自添加到對應(yīng)list。如果不考慮通知過程的復(fù)雜性,這一邏輯應(yīng)包含在方便調(diào)用的方法中,只需要傳入指向新增animal對象的引用即可,這就是通知監(jiān)聽器的邏輯實現(xiàn)封裝在notifyAnimalAddedListeners函數(shù)中的原因,這一點在addAnimal的實現(xiàn)中也提到過。

除了notify函數(shù)的邏輯問題,需要強調(diào)一下對notify函數(shù)可見性的爭議問題。在經(jīng)典的觀察者模型中,如GoF在設(shè)計模式一書中第301頁所說,notify函數(shù)是public型的,然而盡管在經(jīng)典模式中用到,這并不意味著必須是public的。選擇可見性應(yīng)該基于應(yīng)用,例如本文的動物園的例子,notify函數(shù)是protected類型,并不要求每個對象都可以發(fā)起一個注冊觀察者的通知,只需保證對象能從父類繼承該功能即可。當然,也并非完全如此,需要弄清楚哪些類可以激活notify函數(shù),然后再由此確定函數(shù)的可見性。

接下來需要實現(xiàn)PrintNameAnimalAddedListener類,這個類用System.out.println方法將新增動物的name輸出,具體代碼如下:

public class PrintNameAnimalAddedListener implements AnimalAddedListener { @Overridepublic void updateAnimalAdded (Animal animal) {// Print the name of the newly added animalSystem.out.println("Added a new animal with name '" + animal.getName() + "'");}}

最后要實現(xiàn)驅(qū)動應(yīng)用的主函數(shù):

public class Main { public static void main (String[] args) {// Create the zoo to store animalsZoo zoo = new Zoo();// Register a listener to be notified when an animal is addedzoo.registerAnimalAddedListener(new PrintNameAnimalAddedListener());// Add an animal notify the registered listenerszoo.addAnimal(new Animal("Tiger"));}}

主函數(shù)只是簡單的創(chuàng)建了一個zoo對象,注冊了一個輸出動物name的監(jiān)聽器,并新建了一個animal對象以觸發(fā)已注冊的監(jiān)聽器,最后的輸出為:

Added a new animal with name 'Tiger'

新增監(jiān)聽器

當監(jiān)聽器重新建立并將其添加到Subject時,觀察者模式的優(yōu)勢就充分顯示出來。例如,想添加一個計算動物園中動物總數(shù)的監(jiān)聽器,只需要新建一個具體的監(jiān)聽器類并注冊到Zoo類即可,而無需對zoo類做任何修改。添加計數(shù)監(jiān)聽器CountingAnimalAddedListener代碼如下:

public class CountingAnimalAddedListener implements AnimalAddedListener { private static int animalsAddedCount = 0;@Overridepublic void updateAnimalAdded (Animal animal) {// Increment the number of animalsanimalsAddedCount++;// Print the number of animalsSystem.out.println("Total animals added: " + animalsAddedCount);}}

修改后的main函數(shù)如下:

public class Main { public static void main (String[] args) {// Create the zoo to store animalsZoo zoo = new Zoo();// Register listeners to be notified when an animal is addedzoo.registerAnimalAddedListener(new PrintNameAnimalAddedListener());zoo.registerAnimalAddedListener(new CountingAnimalAddedListener());// Add an animal notify the registered listenerszoo.addAnimal(new Animal("Tiger"));zoo.addAnimal(new Animal("Lion"));zoo.addAnimal(new Animal("Bear"));}}

輸出結(jié)果為:

Added a new animal with name 'Tiger' Total animals added: 1 Added a new animal with name 'Lion' Total animals added: 2 Added a new animal with name 'Bear' Total animals added: 3 

使用者可在僅修改監(jiān)聽器注冊代碼的情況下,創(chuàng)建任意監(jiān)聽器。具有此可擴展性主要是因為Subject和觀察者接口關(guān)聯(lián),而不是直接和ConcreteObserver關(guān)聯(lián)。只要接口不被修改,調(diào)用接口的Subject就無需修改。

匿名內(nèi)部類,Lambda函數(shù)和監(jiān)聽器注冊

Java8的一大改進是增加了功能特性,如增加了lambda函數(shù)。在引進lambda函數(shù)之前,Java通過匿名內(nèi)部類提供了類似的功能,這些類在很多已有的應(yīng)用中仍在使用。在觀察者模式下,隨時可以創(chuàng)建新的監(jiān)聽器而無需創(chuàng)建具體觀察者類,例如,PrintNameAnimalAddedListener類可以在main函數(shù)中用匿名內(nèi)部類實現(xiàn),具體實現(xiàn)代碼如下:

public class Main { public static void main (String[] args) {// Create the zoo to store animalsZoo zoo = new Zoo();// Register listeners to be notified when an animal is addedzoo.registerAnimalAddedListener(new AnimalAddedListener() {@Overridepublic void updateAnimalAdded (Animal animal) {// Print the name of the newly added animalSystem.out.println("Added a new animal with name '" + animal.getName() + "'");}});// Add an animal notify the registered listenerszoo.addAnimal(new Animal("Tiger"));}}

類似的,lambda函數(shù)也可以用以完成此類任務(wù):

public class Main { public static void main (String[] args) {// Create the zoo to store animalsZoo zoo = new Zoo();// Register listeners to be notified when an animal is addedzoo.registerAnimalAddedListener((animal) -> System.out.println("Added a new animal with name '" + animal.getName() + "'"));// Add an animal notify the registered listenerszoo.addAnimal(new Animal("Tiger"));}}

需要注意的是lambda函數(shù)僅適用于監(jiān)聽器接口只有一個函數(shù)的情況,這個要求雖然看起來嚴格,但實際上很多監(jiān)聽器都是單一函數(shù)的,如示例中的AnimalAddedListener。如果接口有多個函數(shù),可以選擇使用匿名內(nèi)部類。

隱式注冊創(chuàng)建的監(jiān)聽器存在此類問題:由于對象是在注冊調(diào)用的范圍內(nèi)創(chuàng)建的,所以不可能將引用存儲一個到具體監(jiān)聽器。這意味著,通過lambda函數(shù)或者匿名內(nèi)部類注冊的監(jiān)聽器不可以撤銷注冊,因為撤銷函數(shù)需要傳入已經(jīng)注冊監(jiān)聽器的引用。解決這個問題的一個簡單方法是在registerAnimalAddedListener函數(shù)中返回注冊監(jiān)聽器的引用。如此一來,就可以撤銷注冊用lambda函數(shù)或匿名內(nèi)部類創(chuàng)建的監(jiān)聽器,改進后的方法代碼如下:

public AnimalAddedListener registerAnimalAddedListener (AnimalAddedListener listener) { // Add the listener to the list of registered listenersthis.listeners.add(listener); return listener;}

重新設(shè)計的函數(shù)交互的客戶端代碼如下:

public class Main { public static void main (String[] args) {// Create the zoo to store animalsZoo zoo = new Zoo();// Register listeners to be notified when an animal is addedAnimalAddedListener listener = zoo.registerAnimalAddedListener((animal) -> System.out.println("Added a new animal with name '" + animal.getName() + "'"));// Add an animal notify the registered listenerszoo.addAnimal(new Animal("Tiger"));// Unregister the listenerzoo.unregisterAnimalAddedListener(listener);// Add another animal, which will not print the name, since the listener// has been previously unregisteredzoo.addAnimal(new Animal("Lion"));}}

此時的結(jié)果輸出只有Added a new animal with name 'Tiger',因為在第二個animal加入之前監(jiān)聽器已經(jīng)撤銷了:

Added a new animal with name 'Tiger'

如果采用更復(fù)雜的解決方案,register函數(shù)也可以返回receipt類,以便unregister監(jiān)聽器調(diào)用,例如:

public class AnimalAddedListenerReceipt { private final AnimalAddedListener listener;public AnimalAddedListenerReceipt (AnimalAddedListener listener) {this.listener = listener;}public final AnimalAddedListener getListener () {return this.listener;}}

receipt會作為注冊函數(shù)的返回值,以及撤銷注冊函數(shù)輸入?yún)?shù),此時的zoo實現(xiàn)如下所示:

public class ZooUsingReceipt { // ...Existing attributes and constructor...public AnimalAddedListenerReceipt registerAnimalAddedListener (AnimalAddedListener listener) {// Add the listener to the list of registered listenersthis.listeners.add(listener);return new AnimalAddedListenerReceipt(listener);}public void unregisterAnimalAddedListener (AnimalAddedListenerReceipt receipt) {// Remove the listener from the list of the registered listenersthis.listeners.remove(receipt.getListener());}// ...Existing notification method...}

上面描述的接收實現(xiàn)機制允許保存信息供監(jiān)聽器撤銷時調(diào)用的,也就是說如果撤銷注冊算法依賴于Subject注冊監(jiān)聽器時的狀態(tài),則此狀態(tài)將被保存,如果撤銷注冊只需要指向之前注冊監(jiān)聽器的引用,這樣的話接收技術(shù)則顯得麻煩,不推薦使用。

除了特別復(fù)雜的具體監(jiān)聽器,最常見的注冊監(jiān)聽器的方法是通過lambda函數(shù)或通過匿名內(nèi)部類注冊。當然,也有例外,那就是包含subject實現(xiàn)觀察者接口的類和注冊一個包含調(diào)用該引用目標的監(jiān)聽器。如下面代碼所示的案例:

public class ZooContainer implements AnimalAddedListener { private Zoo zoo = new Zoo();public ZooContainer () {// Register this object as a listenerthis.zoo.registerAnimalAddedListener(this);}public Zoo getZoo () {return this.zoo;}@Overridepublic void updateAnimalAdded (Animal animal) {System.out.println("Added animal with name '" + animal.getName() + "'");}public static void main (String[] args) {// Create the zoo containerZooContainer zooContainer = new ZooContainer();// Add an animal notify the innerally notified listenerzooContainer.getZoo().addAnimal(new Animal("Tiger"));}}

這種方法只適用于簡單情況而且代碼看起來不夠?qū)I(yè),盡管如此,它還是深受現(xiàn)代Java開發(fā)人員的喜愛,因此了解這個例子的工作原理很有必要。因為ZooContainer實現(xiàn)了AnimalAddedListener接口,那么ZooContainer的實例(或者說對象)就可以注冊為AnimalAddedListener。ZooContainer類中,該引用代表當前對象即ZooContainer的一個實例,所以可以被用作AnimalAddedListener。

通常,不是要求所有的container類都實現(xiàn)此類功能,而且實現(xiàn)監(jiān)聽器接口的container類只能調(diào)用Subject的注冊函數(shù),只是簡單把該引用作為監(jiān)聽器的對象傳給register函數(shù)。在接下來的章節(jié)中,將介紹多線程環(huán)境的常見問題和解決方案。

OneAPM 為您提供端到端的Java 應(yīng)用性能解決方案,我們支持所有常見的 Java 框架及應(yīng)用服務(wù)器,助您快速發(fā)現(xiàn)系統(tǒng)瓶頸,定位異常根本原因。分鐘級部署,即刻體驗,Java 監(jiān)控從來沒有如此簡單。想閱讀更多技術(shù)文章,請訪問OneAPM 官方技術(shù)博客。

以上內(nèi)容給大家介紹了使用Java8實現(xiàn)觀察者模式的方法(上)的相關(guān)內(nèi)容,下篇文章給大家介紹使用java8實現(xiàn)觀察者模式的方法(下),感興趣的朋友繼續(xù)學(xué)習(xí)吧,希望對大家有所幫助!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 义马市| 水富县| 固阳县| 浦江县| 怀远县| 泽州县| 台山市| 崇州市| 乐亭县| 黎川县| 南皮县| 天门市| 灯塔市| 犍为县| 建昌县| 隆化县| 于田县| 习水县| 彭泽县| 弥勒县| 阳江市| 阿城市| 汉中市| 灵璧县| 马山县| 临澧县| 永年县| 蛟河市| 安丘市| 青岛市| 青海省| 如皋市| 龙游县| 苍南县| 龙井市| 柏乡县| 新民市| 英德市| 招远市| 长乐市| 宜春市|