第四篇:观察者模式

    xiaoxiao2021-03-25  128

    今天我们来谈谈观察者模式,这个模式其实无论是在编程还是在生活中都是随处可见的,就比如说,你在烧一壶水,某个时间段后,当水壶“叮”的一声,你知道水开了,这个时候,你可能会去拔掉电源插头,可能会去拿起水壶泡一壶茶;当然这一切到底做不做都取决于你,水壶已经履行了它的义务,“将水烧开再叮的一声通知你...”

    又比如说,你在优酷订阅了某个有趣的节目,当节目有更新时,你会收到通知,当然,还有其它订阅了该频道的用户也会收到通知...等等等等,那么我们通过这两个列子来看看,观察者模式它是怎么定义的呢?它有些什么样的角色呢?让我们画个图来更直观的了解下吧!

    画图之前,我们先说,观察者模式有哪些角色和定义 ; 

    1:观察者 ; 2:被观察者;3:事件触发;4:事件通知

    我们先来将烧水这个例子画个图看看!

    在这个图中,你在观察着水壶,而水壶将水烧开后,发出叮的一声告诉你水开了,你应该做些什么了! (注:面向对象中,我们可以将叮的一声看成一个行为,也就是一个方法)

    那么,这里面的几个角色和动作,我们可以定义为:

    你 ===== 观察者;水壶 ===== 被观察者;水开了 ===== 事件触发;叮的一声 ===== 事件通知

    这个例子有点抽象,让我们来看一个更具体的吧!

    我们用第二个例子来画图!

    我们先看看如果不用观察者模式,代码可能会是什么样子...

    /**某个节目*/ class Broadcast { //节目组名称 private String name ; //发布节目内容 private String content; public String getName() {return name;} public void setName(String name) {this.name = name;} public String getContent() {return content;} public void setContent(String content) {this.content = content;} } /**用户*/ class User { /**更新用户的通知栏显示内容方法 * @param content 通知内容 * */ public void notifyBar(String content){ System.out.println("收到通知:"+content); } } /**栏目发布中心*/ public class PublishController { /**发布节目*/ public void publish(Broadcast broadcast){ System.out.println("节目发布!"); //我们假设就只有一个用户,给用户发通知 User user = new User(); user.notifyBar("您关注的\""+broadcast.getName()+"\"发布了新的节目:"+broadcast.getContent()); } } public class Test { public static void main(String[] args) { Broadcast broadcast = new Broadcast(); broadcast.setName("社会百态"); broadcast.setContent("论未成年人的保护重要性"); PublishController pc = new PublishController(); pc.publish(broadcast); } }

    输出结果:

    **************************************************************************************

    节目发布! 收到通知:您关注的"社会百态"发布了新的节目:论未成年人的保护重要性 **************************************************************************************

    让我们接着看随着需求的变化,代码可能要发生怎样的改变...

    /**栏目发布中心*/ public class PublishController { /**发布节目*/ public void publish(Broadcast broadcast){ System.out.println("节目发布!"); //我们假设就只有一个用户,给用户发通知 User user = new User(); user.notifyBar("您关注的\""+broadcast.getName()+"\"发布了新的节目:"+broadcast.getContent()); //随着产品影响力的扩大和用户反馈,需求的迭代导致功能修改不可避免的发生,无论怎样,我们都得继续改动这个方法的代码 //需求1:某些质量比较好的节目,一旦发布就得推送到网站首页,好,咱们加代码! Website ws = new Website(); ws.checkQuality(broadcast.getContent()); //需求2:每发布一次节目,节目制作方可获得100点积分... //XXX xx = new Xxx(); //xx.xxx(); //需求3:每发布一次节目,节目制作方将获得一笔鼓励费用... //需求4...以后可能还有各种各样的需求... } } 是的,我们看到了随着需求的变化对PublishController类带来的变化,看到了它的设计存在着众多的问题;

    1:面向对象设计原则,多扩展开发,对修改关闭,很显然,它完全违背了这个思想,每次扩展功能都要修改既有代码,导致测试同童鞋每次都需要将全部功能测试一遍!;

    2:与各种类严重耦合,我们说过,要依赖与抽象不依赖实现,这是对为今后能扩展功能的最基本的要求;

    3:违背面向对象单一职责原则,它不应该知道具体如何去通知用户,如何去更新积分,如何去更新网站首页...更好的做法是,它只负责对某个抽象类发送一个通知,并告诉它们更新了什么东西,至于你接下来要做什么,我可不管!

    好吧,不多说,我们马上来将上面的代码改造成观察者模式!

    增加观察者和被观察者接口:

    /**表示某个类具备观察的能力 (也可以称这个类为监听对象)*/ public interface Observer { /**被观察者对观察者的事件通知方法,也可以称之为回调方法*/ void update(Broadcast broadcast); } /**表示某个对象具备被观察的能力*/ public interface Observerable { /**注册一个观察者*/ void registryObserver(Observer observer); /**移除一个观察者*/ void removeObserver(Observer observer); /**通知所有观察者*/ void notifyAllObserver(Broadcast broadcast); }

    对user和Website的改动:

    /**用户*/ class User implements Observer{ //保存被观察者的引用,这样一来,后期我可以自己决定是否要取消关注 private Observerable observerable; public User( Observerable observerable ) { this.observerable = observerable; //添加对被观察者的监听 observerable.registryObserver(this); } /**更新用户的通知栏显示内容方法 * @param content 通知内容 * */ public void notifyBar(String content){ System.out.println("收到通知:"+content); } /**增加回调方法*/ @Override public void update(Broadcast broadcast) { notifyBar(broadcast.getContent()); } } /**表示整个站点*/ class Website implements Observer{ //保存被观察者的引用,这样一来,后期我可以自己决定是否要取消关注 private Observerable observerable; public Website( Observerable observerable ) { this.observerable = observerable; //添加对被观察者的监听 observerable.registryObserver(this); } public void checkQuality(String content){ System.out.println("节目内容检查通过,被鉴定为优质节目..."); System.out.println("站点某个位置数据更新为:"+content); } /**增加回调方法*/ @Override public void update(Broadcast broadcast) { checkQuality(broadcast.getContent()); } }对PublishController的改动: /**栏目发布中心*/ public class PublishController implements Observerable{ //增加一个集合用于维护所有的观察者 List<Observer> observers ; public PublishController() { observers = new ArrayList<>(); } /**发布节目*/ public void publish(Broadcast broadcast){ System.out.println("节目发布!"); notifyAllObserver(broadcast); } @Override public void registryObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if(index!=-1){ observers.remove(index); } } /**通知所有观察者*/ @Override public void notifyAllObserver(Broadcast broadcast) { for (Observer observer : observers) { observer.update(broadcast); } } }

    测试一下:

    public class Test { public static void main(String[] args) { Broadcast broadcast = new Broadcast(); broadcast.setName("社会百态"); broadcast.setContent("论未成年人的保护重要性"); PublishController pc = new PublishController(); //可以随意增加监听用户了! User user = new User(pc); User user2 = new User(pc); User user3 = new User(pc); Website ws = new Website(pc); pc.publish(broadcast); //需求改动,说:不再需要更新站点...简单,我们只需要移除站点监听对象就OK啦! pc.removeObserver(ws); System.out.println("*******************************************"); pc.publish(broadcast); //需求改动,说:要增加一个新的功能... //简单,我们只需要这样做 //Observer xxx = new xxx(); //pc.registryObserver(xxx); } }

    内容输出:

    ****************************************************************************

    节目发布! 收到通知:论未成年人的保护重要性 收到通知:论未成年人的保护重要性 收到通知:论未成年人的保护重要性 节目内容检查通过,被鉴定为优质节目... 站点某个位置数据更新为:论未成年人的保护重要性 ******************************************* 节目发布! 收到通知:论未成年人的保护重要性 收到通知:论未成年人的保护重要性 收到通知:论未成年人的保护重要性 ****************************************************************************

    这样一来,后面我们需要扩展功能,只需要增加一个监听对象,而要取消某个功能,只需要移除监听就可以啦!再看我们的PublishController类,无论对于以后的功能扩展和取消都完全不需要改变了!而且与具体的实体类,比如User,Website解耦了!在它世界里,耦合的对象只剩下Observer接口;

    最后,让我们对观察者模式来下一个定义:

    观察者模式:在对象与对象之间建立一对多的关系,当被监听的对象某些状态发生改变或者某个行为被触发,监听它的多的一方将自动收到通知并采取更新动作!

    java也提供了Observer和Observable;只不过它的Observable是个类,你需要去继承,而我们的是个接口,可以更灵活的实现 ”多继承“!

    转载请注明原文地址: https://ju.6miu.com/read-9258.html

    最新回复(0)