观察者模式可以说是众多设计模式中,最容易理解的设计模式之一了,观察者模式在Spring中也随处可见,面试的时候,面试官可能会问,嘿,你既然读过Spring源码,那你说说Spring中运用的设计模式吧,你可以自信的告诉他,Spring中的ApplicationListener就运用了观察者模式。
让我们一步一步来,首先我们要知道到底什么是观察者模式,用Java是如何实现的,在这里,我将会用三种方式来实现观察者模式。
什么是观察者模式在现实生活中,观察者模式处处可见,比如
看新闻,只要新闻开始播放了,就会把新闻推送给订阅了新闻的用户,在这里,新闻就是【被观察者】,而用户就是【观察者】。
微信公众号,如果一个用户订阅了某个公众号,那么便会收到公众号发来的消息,那么,公众号就是【被观察者】,而用户就是【观察者】。
热水器,假设热水器由三部分组成,热水器,警报器,显示器,热水器仅仅负责烧水,当水温到达设定的温度后,通知警报器,警报器发出警报,显示器也需要订阅热水器的烧水事件,从而获得水温,并显示。热水器就是【被观察者】,警报器,显示器就是【观察者】。
在这里,可以看到,【观察者】已经失去自主的权利,只能被动的接收来自【被观察者】的事件,无法主动观察。【观察者】成为了“受”,而【被观察者】成为了“攻”。【被观察者】只是通知【观察者】,不关心【观察者】收到通知后,会执行怎样的动作。
而在设计模式中,又把【被观察者】称为【主题】。
在观察者设计模式中,一般有四个角色:
抽象主题角色(Subject)
具体主题角色(ConcreteSubject)
抽象观察者角色(Observer)
具体观察者角色(ConcreteObserver)
其中,【主题】需要有一个列表字段,用来保存【观察者】的引用,提供两个方法(虚方法),即【删除观察者】【增加观察者】,还需要提供一个给客户端调用的方法,通知各个【观察者】:你们关心(订阅)的事件已经推送给你们了。
下面,我就用三种方式来实现观察者模式。
经典 public class News { private String title; private String content; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }此类不属于观察者模式必须的类,用来存放事件的信息。
public interface Subject { List<People> peopleList = new ArrayList<>(); default void add(People people) { peopleList.add(people); } default void remove(People people) { peopleList.remove(people); } void update(); }抽象主题角色,在这个角色中,有一个字段peopleList,用来保存【观察者】的引用,同时定义了两个接口,这是Java8默认接口实现的写法。这两个接口是给客户端调用的,用来【删除观察者】【增加观察者】,还提供一个方法,此方法需要被【具体主题角色】重写,用来通知各个【观察者】。
public class NewsSubject implements Subject{ public void update() { for (People people : peopleList) { News news = new News(); news.setContent("今日在大街上,有人躲在草丛中袭击路人,还大喊“德玛西亚万岁”"); news.setTitle("德玛西亚出现了"); people.update(news); } } }具体主题角色,重写了【抽象主题角色】的方法,循环列表,通知各个【观察者】。
public interface People { void update(News news); }抽象观察者角色,定义了一个接口,【具体观察者角色】需要重写这个方法。
下面就是【具体观察者角色】了:
public class PeopleA implements People { @Override public void update(News news) { System.out.println("这个新闻真好看"); } } public class PeopleB implements People { @Override public void update(News news) { System.out.println("这个新闻真无语"); } } public class PeopleC implements People { @Override public void update(News news) { System.out.println("这个新闻真逗"); } }客户端:
public class Main { public static void main(String[] args) { Subject subject = new NewsSubject(); subject.add(new PeopleA()); subject.add(new PeopleB()); subject.add(new PeopleC()); subject.update(); } }运行:
我们学习设计模式,必须知道设计模式的优缺点,那么观察者设计模式的优缺点是什么呢?
优点:
【主题】和【观察者】通过抽象,建立了一个松耦合的关系,【主题】只知道当前有哪些【观察者】,并且发送通知,但是不知道【观察者】具体会执行怎样的动作。这也很好理解,比如 微信公众号推送了一个消息过来,它不知道你会采取如何的动作,是 微笑的打开,还是愤怒的打开,或者是直接把消息删了,又或者把手机扔到洗衣机洗刷刷。
符合开闭原则,如果需要新增一个【观察者】,只需要写一个类去实现【抽象观察者角色】即可,不需要改动原来的代码。
缺点:
客户端必须知道所有的【观察者】,并且进行【增加观察者】和【删除观察者】的操作。
如果有很多【观察者】,那么所有的【观察者】收到通知,可能需要花费很久时间。
当然以上优缺点,是最直观的,可以很容易理解,并且体会到的。其他优缺点,可以自行百度。
Lambda