第四篇:观察者模式

来源:互联网 发布:qq空间怎么推广淘宝 编辑:程序博客网 时间:2024/06/05 08:32

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

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

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

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);}/**增加回调方法*/@Overridepublic 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);}/**增加回调方法*/@Overridepublic 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);}@Overridepublic void registryObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {int index = observers.indexOf(observer);if(index!=-1){observers.remove(index);}}/**通知所有观察者*/@Overridepublic 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是个类,你需要去继承,而我们的是个接口,可以更灵活的实现 ”多继承“!


1 0
原创粉丝点击