大家好,我是悟空。正在热播的脱口秀大会大家肯定都看过了,所以这次我就带大家看看大会上的观察员模式。1、脱口秀首先是脱口秀的角色划分:我们把脱口秀演员看成一个Observable。4个笑声+180个观众,作为观察员。喜剧演员的职责:当脱口秀表演者表现出色时,点亮灯光,以示非常搞笑。观众职责:当脱口秀表演者表现出色时,拿起他们手中的遥控器,按下按钮,表示喜欢。这种场景非常符合观察者模式。简单地说,就是一群观察者观察被观察的对象,并对被观察的对象做出反应。说完上面的例子,想必大家对观察者模式有了初步的印象。那我们就来看看观察者模式在编程世界里是什么样子的。2.观察者模式GoF设计模式书中说:定义对象之间一对多的依赖关系。当一个对象改变状态时,所有依赖的对象都会自动收到通知。这就是观察者模式。.观察者模式还有很多其他的名字,比如发布订阅、监听回调等等,其实只要场景符合上面的描述,都可以称为观察者模式。JavaAPI内置了观察者模式,使用起来非常方便。用法:java.util包中包含最基本的Observer接口(观察者接口)和Observable类(被观察父类)。此外,它们之间可以通过推或拉的方式传输数据。另外很重要的一点:被观察者和被观察者之间的关系是一对多的。如上面脱口秀的例子,有很多观众,一次只有一个演员(或脱口秀组合)。3.观察如何工作?你只需要这个类来继承Observable类。让我向您展示这个Observable类的组成。添加观察者,我们先想一下,当我们要观察别人的时候,是否需要添加为别人的观察者。然后我们需要一个添加观察者的方法。Observale为我们提供了一种添加他人观察者的方法:addObserver。存放观察者当有很多人想成为观察者时,有没有地方存放这些观察者?Observable为我们提供了一个存放所有观察者的地方:一个Vector集合。移除观察者当我们不想被别人观察时,移除它即可。Observable为我们提供了移除观察者的方法:deleteObserver。被观察者如何发送通知?当被观察对象要告诉观察者他的状态发生了变化,他是否应该发送一个通知?Observable为我们提供了两种方法:notifyObservers()或notifyObservers(Objectarg)。区别是一个带参数和一个不带参数。不带参数的方法经常被观察者通过pull方式获取数据。如下图所示,通过推送通知观察者。那么通知的具体内容是什么?说白了就是三个步骤:被观察对象首先判断自己的状态是否发生了变化。从向量集合中获取所有添加的观察者。循环遍历观察者并调用观察者的更新方法。看源码更清楚,注释都加上了。publicvoidnotifyObservers(Objectvar1){Object[]var2;synchronized(this){//调用setChange()方法时,this.changed=trueif(!this.changed){return;}//获取所有观察者var2=this.obs.toArray();//重置变化状态this.clearChanged();}//循环通知观察者for(intvar3=var2.length-1;var3>=0;--var3){((Observer)var2[var3]).update(this,var1);}}为什么会有setChanged?在观察者发送通知之前,被观察对象会调用setChanged()方法来标记状态已经改变。protectedsynchronizedvoidclearChanged(){this.changed=false;}为什么要调用这个?可以不叫吗?当被观察对象调用notifyObservers方法时,会判断状态是否发生了变化。如果没有变化,则不会通知观察者。这样做的好处:更灵活地通知观察员。如果不想一直通知观察者,可以适当控制setChanged方法的调用。其他:也可以使用clearChanged来重置改变后的状态,使用hasChanged方法获取改变后的状态。4.观察者是如何工作的?其实很简单。观察者实现Observer接口后即可成为观察者。publicinterfaceObserver{voidupdate(Observablevar1,Objectvar2);}然后观察者实现update方法,对被观察对象调用。关于push模式和pull模式的小插曲:如果想使用push模式,可以调用带参数的notifyObservers方法,将参数传递给观察者。如果要使用pull模式,需要主动调用observer的getdata方法,通知observer有无参数都可以。5.代码实现我们将笑话主持人定义为Leader类,观众定义为Viewer类,脱口秀演员定义为Actor类。笑的人都在看演员表演单口相声,需要成为演员的旁观者。只需调用actor.addObserver(leader)。观众类似,调用actor.addObserver(viewer)即可。根据上面解释的原则,笑者和观众必须继承观察者接口,然后实现update方法。如下图:收到通知后,做出相应的响应,比如拍灯。每个actor的stalk完成后,会调用setChanged()方法和notifyObservers(参数)通知观察者,然后触发所有观察者的update方法。我们来看一下演员的通知代码:执行结果如下,王冕的表演很精彩,笑点亮了!下载源码,后台回复公众号:观察者。嗯,观察者模式还是挺有意思的。那么它在电子商务中是如何应用的呢?6.关于设计模式,在观察者和被观察者的工作原理上有一些坑。不知道大家有没有注意到?需要将观察者添加到被观察者的特定集合中才能观察,相当于是面向细节的,违背了面向抽象的原则。Observable是类,不是接口,Observable没有实现接口,违反了面向接口编程。必须有一个类来继承Observable。如果一个类拥有Observer类的功能,又想拥有另一个类的功能,就会进退两难,因为Java不支持多重继承,限制了Observable的复用潜力。另外,ObserverAPI中的setChanged()方法是protected的(定义为protected方法),所以除非你继承Observable,否则你不能创建Observable实例并将它们组合成你自己的对象。它违反了“多使用组合,少使用继承”的原则。七、架构设计中的问题问题一:上面的观察者模式是一种同步阻塞的方式。被观察者需要等待观察者执行完毕,才能执行后续代码。如何异步通知观察者?方案一:启动一个线程调用notifyObservers方法。方案二:GoogleGuavaEventBus框架的设计思路问题二:如何跨进程通信?方案一:我们看到observed每次都要调用observer的update方法来通知observer,那么跨进程怎么办呢?我们可以通过同步调用RPC接口来实现。方案二:消息队列,可以有多个消费者和生产者,消费者订阅消息,类似于观察者。但是,消息队列的引入增加了维护成本。问题三:如何跨机器通信?或者引入消息队列。8、在电子商务的应用中,商品库存可以作为观察者,商品入库单可以作为观察者。当商品库存发生变化时,需要生成商品入库单,可以使用观察者模式。商品入库单与商品库存解耦。如果需要生成其他类型的存储订单并发送消息给管理员,可以直接添加观察者。九。后记本文通过脱口秀大会讲解观察者模式,涉及三个角色,笑者、观众、脱口秀演员。然后详细解释了观察者和被观察者的工作原理,并讨论了该模式的设计模式相关问题。然后从架构设计的角度分析了观察者模式引入的问题:同步调用、跨进程通信、跨机器通信。最后简单说一下在电子商务中的应用场景,希望大家留言讨论。本文转载自微信公众号“悟空聊天架构”,可通过以下二维码关注。转载本文请联系悟空聊天架构公众号。
