使用Lambdas重構觀察者設計模式
當某個物件(稱為主體)需要在某些事件發生時(例如,狀態更改)自動通知其他物件(稱為觀察者)的列表時,觀察者設計模式是一種常見的解決方案。在使用GUI應用程式時,您通常會遇到此模式。您在GUI元件(如按鈕)上註冊一組觀察者。如果單擊該按鈕,則會通知觀察者並執行特定操作。但觀察者模式不限於GUI。例如,觀察者設計模式也適用於多個交易者(觀察者)可能希望對股票(主體)的價格變化作出反應的情況。
讓我們為Twitter等應用程式設計並實現自定義通知系統。這個概念很簡單:幾家報社(NY Times,The Guardian和Le Monde )訂閱了新聞推文,如果推文包含特定關鍵詞,可能希望收到通知。
觀察者模式:不使用Lambda表示式
步驟1:建立一個Observer 介面,對不同的觀察者進行分組。它只有一個名為inform的 方法,當有新的推文可供主題(Feed)呼叫時。
<b>public</b> <b>interface</b> Observer { <b>void</b> inform(String tweet); }
第2步:讓我們建立不同的觀察者(這裡是三份報紙),為包含在推文中的每個不同的關鍵詞產生不同的行動。
<b>public</b> <b>class</b> NYTimes implements Observer{ @Override <b>public</b> <b>void</b> inform(String tweet) { <b>if</b>(tweet != <b>null</b> && tweet.contains(<font>"money"</font><font>)){ System.out.println(</font><font>"Breaking news in NY!"</font><font> + tweet); } } } <b>public</b> <b>class</b> Guardian implements Observer{ @Override <b>public</b> <b>void</b> inform(String tweet) { <b>if</b>(tweet != <b>null</b> && tweet.contains(</font><font>"queen"</font><font>)){ System.out.println(</font><font>"Yet another news in London... "</font><font> + tweet); } } } <b>public</b> <b>class</b> LeMonde implements Observer{ @Override <b>public</b> <b>void</b> inform(String tweet) { <b>if</b>(tweet != <b>null</b> && tweet.contains(</font><font>"wine"</font><font>)){ System.out.println(</font><font>"Today cheese, wine and news! "</font><font> + tweet); } } } </font>
第3步:讓我們來定義 主題 介面。
<b>public</b> <b>interface</b> Subject{ <b>void</b> registerObserver(Observer o); <b>void</b> notifyObservers(String tweet); }
第4步:這是一個非常簡單的實現:feed保留一個內部觀察者列表,然後它可以在推文到達時通知。
<b>public</b> <b>class</b> Feed implements Subject{ <b>private</b> <b>final</b> List<Observer> observers = <b>new</b> ArrayList<>(); <b>public</b> <b>void</b> registerObserver(Observer o) { <b>this</b>.observers.add(o); } <b>public</b> <b>void</b> notifyObservers(String tweet) { observers.forEach(o -> o.inform(tweet)); } }
第5步:我們現在可以建立一個演示應用程式來連線主題和觀察者。
<b>public</b> <b>static</b> <b>void</b> main(String[] args) { Feed f = <b>new</b> Feed(); f.registerObserver(<b>new</b> NYTimes()); f.registerObserver(<b>new</b> Guardian()); f.registerObserver(<b>new</b> LeMonde()); f.notifyObservers(<font>"The queen said her favourite book is Java 8 in Action!"</font><font>); } </font>
觀察者模式:使用Lambda表示式
請注意,實現Observer 介面的不同類都提供了單個方法的實現:inform() 。
當推文到達時,它們只是包裹著一段行為,Lambda表示式專門用於刪除該樣板。您可以直接傳遞lambda表示式來表示要執行的行為,而不是顯式地例項化三個觀察物件:
<b>public</b> <b>static</b> <b>void</b> main(String[] args) { Feed feedLambda = <b>new</b> Feed(); feedLambda.registerObserver((String tweet) -> { <b>if</b>(tweet != <b>null</b> && tweet.contains(<font>"money"</font><font>)){ System.out.println(</font><font>"Breaking news in NY! "</font><font> + tweet); } }); feedLambda.registerObserver((String tweet) -> { <b>if</b>(tweet != <b>null</b> && tweet.contains(</font><font>"queen"</font><font>)){ System.out.println(</font><font>"Yet another news in London... "</font><font> + tweet); } }); feedLambda.notifyObservers(</font><font>"Money money money, give me money!"</font><font>); } </font>
我們應該一直使用lambda表示式嗎?答案是不!在我們描述的示例中,lambda表示式工作得很好,因為要執行的行為很簡單,因此它們有助於刪除樣板程式碼。但觀察者可能更復雜:他們可能有狀態,定義了幾種方法等。在這種情況下,你應該堅持使用類模板的樣板程式碼。