国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類(lèi)導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務(wù)器之家 - 編程語(yǔ)言 - JAVA教程 - 使用Java8實(shí)現(xiàn)觀察者模式的方法(上)

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

2020-04-03 14:59mrr JAVA教程

本文給大家介紹使用java8實(shí)現(xiàn)觀察者模式的方法,涉及到j(luò)ava8觀察者模式相關(guān)知識(shí),對(duì)此感興趣的朋友一起學(xué)習(xí)吧

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

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

觀察者模式

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

一個(gè)簡(jiǎn)單的實(shí)例

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

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

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

Zoo類(lèi):即模式中的主題,負(fù)責(zé)存儲(chǔ)動(dòng)物園中的所有動(dòng)物,并在新動(dòng)物加入時(shí)通知所有已注冊(cè)的監(jiān)聽(tīng)器。

Animal類(lèi):代表動(dòng)物對(duì)象。

AnimalAddedListener類(lèi):即觀察者接口。

PrintNameAnimalAddedListener:具體的觀察者類(lèi),負(fù)責(zé)輸出新增動(dòng)物的name。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
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;
}
}

用這個(gè)類(lèi)代表動(dòng)物對(duì)象,接下來(lái)就可以創(chuàng)建AnimalAddedListener接口了:

?
1
2
3
public interface AnimalAddedListener {
public void onAnimalAdded (Animal animal);
}

前面兩個(gè)類(lèi)很簡(jiǎn)單,就不再詳細(xì)介紹,接下來(lái)創(chuàng)建Zoo類(lèi):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 animals
this.animals.add(animal);
// Notify the list of registered listeners
this.notifyAnimalAddedListeners(animal);
}
public void registerAnimalAddedListener (AnimalAddedListener listener) {
// Add the listener to the list of registered listeners
this.listeners.add(listener);
}
public void unregisterAnimalAddedListener (AnimalAddedListener listener) {
// Remove the listener from the list of the registered listeners
this.listeners.remove(listener);
}
protected void notifyAnimalAddedListeners (Animal animal) {
// Notify each of the listeners in the list of registered listeners
this.listeners.forEach(listener -> listener.updateAnimalAdded(animal));
}
}

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

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

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

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

接下來(lái)需要實(shí)現(xiàn)PrintNameAnimalAddedListener類(lèi),這個(gè)類(lèi)用System.out.println方法將新增動(dòng)物的name輸出,具體代碼如下:

?
1
2
3
4
5
6
7
public class PrintNameAnimalAddedListener implements AnimalAddedListener {
@Override
public void updateAnimalAdded (Animal animal) {
// Print the name of the newly added animal
System.out.println("Added a new animal with name '" + animal.getName() + "'");
}
}

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

?
1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main (String[] args) {
// Create the zoo to store animals
Zoo zoo = new Zoo();
// Register a listener to be notified when an animal is added
zoo.registerAnimalAddedListener(new PrintNameAnimalAddedListener());
// Add an animal notify the registered listeners
zoo.addAnimal(new Animal("Tiger"));
}
}

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

Added a new animal with name 'Tiger'

新增監(jiān)聽(tīng)器

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

?
1
2
3
4
5
6
7
8
9
10
public class CountingAnimalAddedListener implements AnimalAddedListener {
private static int animalsAddedCount = 0;
@Override
public void updateAnimalAdded (Animal animal) {
// Increment the number of animals
animalsAddedCount++;
// Print the number of animals
System.out.println("Total animals added: " + animalsAddedCount);
}
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main (String[] args) {
// Create the zoo to store animals
Zoo zoo = new Zoo();
// Register listeners to be notified when an animal is added
zoo.registerAnimalAddedListener(new PrintNameAnimalAddedListener());
zoo.registerAnimalAddedListener(new CountingAnimalAddedListener());
// Add an animal notify the registered listeners
zoo.addAnimal(new Animal("Tiger"));
zoo.addAnimal(new Animal("Lion"));
zoo.addAnimal(new Animal("Bear"));
}
}

輸出結(jié)果為:

?
1
2
3
4
5
6
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)聽(tīng)器注冊(cè)代碼的情況下,創(chuàng)建任意監(jiān)聽(tīng)器。具有此可擴(kuò)展性主要是因?yàn)镾ubject和觀察者接口關(guān)聯(lián),而不是直接和ConcreteObserver關(guān)聯(lián)。只要接口不被修改,調(diào)用接口的Subject就無(wú)需修改。

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main (String[] args) {
// Create the zoo to store animals
Zoo zoo = new Zoo();
// Register listeners to be notified when an animal is added
zoo.registerAnimalAddedListener(new AnimalAddedListener() {
@Override
public void updateAnimalAdded (Animal animal) {
// Print the name of the newly added animal
System.out.println("Added a new animal with name '" + animal.getName() + "'");
}
});
// Add an animal notify the registered listeners
zoo.addAnimal(new Animal("Tiger"));
}
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main (String[] args) {
// Create the zoo to store animals
Zoo zoo = new Zoo();
// Register listeners to be notified when an animal is added
zoo.registerAnimalAddedListener(
(animal) -> System.out.println("Added a new animal with name '" + animal.getName() + "'")
);
// Add an animal notify the registered listeners
zoo.addAnimal(new Animal("Tiger"));
}
}

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

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

?
1
2
3
4
5
public AnimalAddedListener registerAnimalAddedListener (AnimalAddedListener listener) {
// Add the listener to the list of registered listeners
this.listeners.add(listener);
return listener;
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main (String[] args) {
// Create the zoo to store animals
Zoo zoo = new Zoo();
// Register listeners to be notified when an animal is added
AnimalAddedListener listener = zoo.registerAnimalAddedListener(
(animal) -> System.out.println("Added a new animal with name '" + animal.getName() + "'")
);
// Add an animal notify the registered listeners
zoo.addAnimal(new Animal("Tiger"));
// Unregister the listener
zoo.unregisterAnimalAddedListener(listener);
// Add another animal, which will not print the name, since the listener
// has been previously unregistered
zoo.addAnimal(new Animal("Lion"));
}
}

此時(shí)的結(jié)果輸出只有Added a new animal with name 'Tiger',因?yàn)樵诘诙€(gè)animal加入之前監(jiān)聽(tīng)器已經(jīng)撤銷(xiāo)了:

Added a new animal with name 'Tiger'

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

?
1
2
3
4
5
6
7
8
9
public class AnimalAddedListenerReceipt {
private final AnimalAddedListener listener;
public AnimalAddedListenerReceipt (AnimalAddedListener listener) {
this.listener = listener;
}
public final AnimalAddedListener getListener () {
return this.listener;
}
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ZooUsingReceipt {
// ...Existing attributes and constructor...
public AnimalAddedListenerReceipt registerAnimalAddedListener (AnimalAddedListener listener) {
// Add the listener to the list of registered listeners
this.listeners.add(listener);
return new AnimalAddedListenerReceipt(listener);
}
public void unregisterAnimalAddedListener (AnimalAddedListenerReceipt receipt) {
// Remove the listener from the list of the registered listeners
this.listeners.remove(receipt.getListener());
}
// ...Existing notification method...
}

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ZooContainer implements AnimalAddedListener {
private Zoo zoo = new Zoo();
public ZooContainer () {
// Register this object as a listener
this.zoo.registerAnimalAddedListener(this);
}
public Zoo getZoo () {
return this.zoo;
}
@Override
public void updateAnimalAdded (Animal animal) {
System.out.println("Added animal with name '" + animal.getName() + "'");
}
public static void main (String[] args) {
// Create the zoo container
ZooContainer zooContainer = new ZooContainer();
// Add an animal notify the innerally notified listener
zooContainer.getZoo().addAnimal(new Animal("Tiger"));
}
}

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

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

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

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

延伸 · 閱讀

精彩推薦
  • JAVA教程Java實(shí)現(xiàn)驗(yàn)證碼具體代碼

    Java實(shí)現(xiàn)驗(yàn)證碼具體代碼

    這篇文章主要介紹了Java實(shí)現(xiàn)驗(yàn)證碼具體代碼,有需要的朋友可以參考一下 ...

    java教程網(wǎng)2632019-10-24
  • JAVA教程java實(shí)現(xiàn)文件重命名的方法

    java實(shí)現(xiàn)文件重命名的方法

    這篇文章主要介紹了java實(shí)現(xiàn)文件重命名的方法,涉及java針對(duì)文件的重命名操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下 ...

    zzt06051932019-12-27
  • JAVA教程java實(shí)現(xiàn)圖片裁切的工具類(lèi)實(shí)例

    java實(shí)現(xiàn)圖片裁切的工具類(lèi)實(shí)例

    這篇文章主要介紹了java實(shí)現(xiàn)圖片裁切的工具類(lèi)實(shí)例,涉及Java針對(duì)圖片的讀取、修改等相關(guān)操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下 ...

    5iasp3722020-01-15
  • JAVA教程解析Java編程中對(duì)于包結(jié)構(gòu)的命名和訪問(wèn)

    解析Java編程中對(duì)于包結(jié)構(gòu)的命名和訪問(wèn)

    這篇文章主要介紹了Java編程中對(duì)于包結(jié)構(gòu)的命名和訪問(wèn),是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下 ...

    爪哇小博2572020-03-13
  • JAVA教程初識(shí)JAVA數(shù)組

    初識(shí)JAVA數(shù)組

    java語(yǔ)言中,數(shù)組是一種最簡(jiǎn)單的復(fù)合數(shù)據(jù)類(lèi)型。數(shù)組是有序數(shù)據(jù)的集合,數(shù)組中的每個(gè)元素具有相同的數(shù)據(jù)類(lèi)型,可以用一個(gè)統(tǒng)一的數(shù)組名和下標(biāo)來(lái)唯一地...

    hebedich1452019-11-27
  • JAVA教程java計(jì)算自冪數(shù)和水仙花數(shù)

    java計(jì)算自冪數(shù)和水仙花數(shù)

    對(duì)于一個(gè)正整數(shù)而言,長(zhǎng)度是n,如果它的各位上的數(shù)字的n次方之和正好等于它本身,那么我們稱這樣的數(shù)為自冪數(shù),下面使用JAVA實(shí)現(xiàn)這個(gè)方法 ...

    java教程網(wǎng)4892019-11-13
  • JAVA教程Java正則表達(dá)式匹配電話格式

    Java正則表達(dá)式匹配電話格式

    正則表達(dá)式是由普通的字符以及特殊字符組成的文字模式,用來(lái)在查找文字主體時(shí)待匹配的一個(gè)或多個(gè)字符串。本文給大家介紹java正則表達(dá)式匹配電話格式...

    wangdnbre1442020-03-04
  • JAVA教程Java封裝好的mail包發(fā)送電子郵件的類(lèi)

    Java封裝好的mail包發(fā)送電子郵件的類(lèi)

    本文給大家分享了2個(gè)java封裝好的mail包發(fā)送電子郵件的類(lèi),并附上使用方法,小伙伴們可以根據(jù)自己的需求自由選擇。 ...

    hebedich3702020-03-21
主站蜘蛛池模板: 欧美 日韩 国产 一区 | www.成人 | 亚洲精品日本 | 精品在线一区二区 | 黄色一级免费大片 | 美女午夜影院 | 精品国偷自产国产一区 | 亚洲视频综合 | 成人久久精品 | 中文字幕亚洲专区 | 日韩精品久久久 | 99久久国产免费 | 久久大陆 | 精品一区二区久久久久黄大片 | 日韩在线| 欧美成人毛片 | 奇米久久| 精品国产髙清在线看国产毛片 | 精品在线一区二区三区 | 欧美一区二区在线刺激视频 | 免费观看国产视频在线 | 久久久精品国产99久久精品芒果 | 国产精品久久久久免费 | 欧美日韩一区二区三区在线电影 | 精品久久久久久久久久久久久久 | 91在线视频观看 | 国产情侣免费视频 | 欧美精品91| 电影在线观看免费 | 亚洲欧美日韩精品久久亚洲区 | 欧美成人免费视频 | 日韩欧美在线观看 | 国产亚洲精品久久久久久 | 中国a毛片 | 懂色av成人一区二区三区 | 久久久综合色 | 亚洲国产精品一区二区第一页 | av电影免费观看 | 欧美三级网址 | 午夜精品福利电影 | 精品国产欧美一区二区 |