当某个对象(称为主体)需要在某些事件发生时(例如,状态更改)自动通知其他对象(称为观察者)的列表时,观察者设计模式是一种常见的解决方案。在使用GUI应用程序时,您通常会遇到此模式。您在GUI组件(如按钮)上注册一组观察者。如果单击该按钮,则会通知观察者并执行特定操作。但观察者模式不限于GUI。例如,观察者设计模式也适用于多个交易者(观察者)可能希望对股票(主体)的价格变化作出反应的情况。
让我们为Twitter等应用程序设计并实现自定义通知系统。这个概念很简单:几家报社(NY Times,The Guardian和Le Monde)订阅了新闻推文,如果推文包含特定关键词,可能希望收到通知。
观察者模式:不使用Lambda表达式
步骤1:创建一个Observer接口,对不同的观察者进行分组。它只有一个名为inform的方法,当有新的推文可供主题(Feed)调用时。
public interface Observer { void inform(String tweet); }
|
第2步:让我们创建不同的观察者(这里是三份报纸),为包含在推文中的每个不同的关键词产生不同的行动。
public class NYTimes implements Observer{ @Override public void inform(String tweet) { if(tweet != null && tweet.contains("money")){ System.out.println("Breaking news in NY!" + tweet); } } }
public class Guardian implements Observer{ @Override public void inform(String tweet) { if(tweet != null && tweet.contains("queen")){ System.out.println("Yet another news in London... " + tweet); } } }
public class LeMonde implements Observer{ @Override public void inform(String tweet) { if(tweet != null && tweet.contains("wine")){ System.out.println("Today cheese, wine and news! " + tweet); } } }
|
第3步:让我们来定义 主题 接口。public interface Subject{ void registerObserver(Observer o); void notifyObservers(String tweet); }
|
第4步:这是一个非常简单的实现:feed保留一个内部观察者列表,然后它可以在推文到达时通知。
public class Feed implements Subject{ private final List<Observer> observers = new ArrayList<>(); public void registerObserver(Observer o) { this.observers.add(o); } public void notifyObservers(String tweet) { observers.forEach(o -> o.inform(tweet)); } }
|
第5步:我们现在可以创建一个演示应用程序来连接主题和观察者。public static void main(String[] args) { Feed f = new Feed(); f.registerObserver(new NYTimes()); f.registerObserver(new Guardian()); f.registerObserver(new LeMonde()); f.notifyObservers("The queen said her favourite book is Java 8 in Action!"); }
|
观察者模式:使用Lambda表达式
请注意,实现Observer接口的不同类都提供了单个方法的实现:inform()。
当推文到达时,它们只是包裹着一段行为,Lambda表达式专门用于删除该样板。您可以直接传递lambda表达式来表示要执行的行为,而不是显式地实例化三个观察对象:
public static void main(String[] args) {
Feed feedLambda = new Feed();
feedLambda.registerObserver((String tweet) -> { if(tweet != null && tweet.contains("money")){ System.out.println("Breaking news in NY! " + tweet); } }); feedLambda.registerObserver((String tweet) -> { if(tweet != null && tweet.contains("queen")){ System.out.println("Yet another news in London... " + tweet); } });
feedLambda.notifyObservers("Money money money, give me money!");
}
|
我们应该一直使用lambda表达式吗?答案是不!在我们描述的示例中,lambda表达式工作得很好,因为要执行的行为很简单,因此它们有助于删除样板代码。但观察者可能更复杂:他们可能有状态,定义了几种方法等。在这种情况下,你应该坚持使用类模板的样板代码。