观察者模式

来源:互联网 发布:vmware14 mac os补丁 编辑:程序博客网 时间:2024/06/07 09:42
观察者模式(Observer Pattern)在项目中经常会被使用到,也被叫做发布订阅模式,也就是说  观察者 =  发布者 + 订阅者

GoF的《设计模式》中对观察者是这样描述的:
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically。
意思就是,定义一个对象之间的一对多的依赖关系,使得当一个对象的状态发生改变的时候,它的所有依赖对象能够自动地被通知以及被更新。这里需要注意原句被动语态的使用,依赖对象不是主动检测,而是被通知和被更新。

观察者模式的UML


可以发现,观察者模式共涉及4种角色:
抽象主题角色(Subject):也就是被观察者角色,它可以在自己的内部添加或删除一个观察者对象,这些观察者对象将作为它的通知对象。
抽象观察者角色(Observer):为所有的观察者对象定义一个共同的接口,指定相同的通知规范。
具体主题角色(Concrete Subject):实现抽象主题角色,是具体真实的被观察者。
具体观察者角色(Concrete Observer):实现抽象观察者角色,是具体真实的观察者,一般有多个,可以定义自己收到被观察者通知后执行的业务逻辑。

其实再抽象一点划分,观察者模式只有两个角色:观察者和被观察者,也就是1个被观察者对应多个观察者的依赖,这也是为什么观察者 = 发布者 + 订阅者。

其中抽象主题角色(Subject)的代码如下
public interface Subject {//注册观察者对象opublic void attach(Observer o);//删除观察者对象opublic void detach(Observer o);//通知观察者public void notifyObserver();}

抽象主题角色(被观察者角色)定义了增加和删除观察者的方法,具体的被观察者需要维护一个观察者列表,当被观察者的状态发生变化的时候,回去主动的通知列表中的观察者。

抽象观察者角色(Observer)的代码实现:
public interface Observer {//接收到通知的操作public void update();}

只有一个update方法,用于接收到来之被观察者通知的时候采取的更新操作,这个方法一般由被观察者调用。

下面是这两个接口的具体实现,也就是具体主题角色(具体被观察者角色)、具体观察者角色
import java.util.LinkedList;import java.util.List;public class ConcreteSubject implements Subject {//用于保存自己的注册对象private List<Observer> observerList = new LinkedList<Observer>();  //方便经常的插入和删除操作/** * 注册一个观察者 */@Overridepublic void attach(Observer o) {// TODO Auto-generated method stubobserverList.add(o);}/** * 取消一个观察者 */@Overridepublic void detach(Observer o) {// TODO Auto-generated method stubobserverList.remove(o);}/** * 通知观察者 */@Overridepublic void notifyObserver() {// TODO Auto-generated method stubfor(Observer obs : observerList ) {obs.update();   //逐个通知观察者}}}

public class ConcreteObserver implements Observer {/** * 接到通知的逻辑 */@Overridepublic void update() {// TODO Auto-generated method stub}}


观察者模式的具体实例
一个典型的例子就是消息订阅,比如,我们在关注了一个某晚报微信公众号,然后每天公众号给我们发送订阅的新闻,这里公众号就是一个被观察者角色,而我们订阅者就是观察者角色

抽象主题角色Subject,也就是公众号的简单抽象定义:
public interface PublicOffcial {//注册订阅消息的用户public void attach(WechatUser user);//取消订阅消息的用户public void detach(WechatUser user);//向用户发送订阅public void notifyObserver(String news);}

抽象观察者角色,这里也就是微信订阅用户的简单抽象
public interface WechatUser {public void readNews(String news);}

实现具体主题角色(真实被观察者),这里是XX晚报的公众号
import java.util.LinkedList;import java.util.List;public class PublicOffcialXXNewPaper implements PublicOffcial {//已订阅用户的列表private List< WechatUser> list = new LinkedList<WechatUser>();/** * 当用户订阅时,加入列表 */@Overridepublic void attach(WechatUser user) {if( ! list.contains(user)) list.add(user);}/** * 用户取消订阅时,从列表清除 */@Overridepublic void detach(WechatUser user) {if(list.contains(user))list.remove(user);}/** * 通知订阅者 */@Overridepublic void notifyObserver(String news) {for(WechatUser user : list ) {user.readNews(news);}}/** * 报社某天的发布新闻工作,包括发送给微信订阅者,可能还要更新网站 * @param news */public void ReleaseNews(String news) {//something to do  e.g: update website..System.out.println("报社发布今日新闻【" + news + "】");notifyObserver(news);}}

具体观察者角色,也就是订阅消息的用户
public class NewsPaperFan implements WechatUser {private String name;public NewsPaperFan(String name) {// TODO Auto-generated constructor stubthis.name = name;}@Overridepublic void readNews(String news) {System.out.println("用户: " + name + "收到订阅的新闻【" + news + "】");}}

以上就是关于消息订阅的观察者模式的实现。
客户端测试类:
public class Client {public static void main(String[] args) {PublicOffcialXXNewPaper newPaper = new PublicOffcialXXNewPaper();WechatUser user1 = new NewsPaperFan("Tony");WechatUser user2 = new NewsPaperFan("Jack");WechatUser user3 = new NewsPaperFan("David");newPaper.attach(user1);newPaper.attach(user2);newPaper.attach(user3);newPaper.ReleaseNews("苹果即将推出IPhone6...");}}

程序输出:
报社发布今日新闻【苹果即将推出IPhone6...】用户: Tony收到订阅的新闻【苹果即将推出IPhone6...】用户: Jack收到订阅的新闻【苹果即将推出IPhone6...】用户: David收到订阅的新闻【苹果即将推出IPhone6...】


综上,可以看出观察者模式的一些优点
  • 观察者和被观察者之间是抽象耦合的。被观察者只能观察到观察者的接口层定义,而不知道自己通知的观察者的具体类型,它们之间属于不同的抽象层面,没有紧密的耦合,容易扩展。
  • 支持像消息订阅这样的广播通信。

一些缺点
  • 如果被观察者需要通知多个观察者,或者通知过程时候耗时,那么上述被观察者的通知过程将会非常耗时。对于这一点,如果通知的过程是相互独立的任务,那么可以使用多线程去完成,使通知在子线程中完成。
  • 如果拥有多个被观察者,并且它们之间存在依赖关系,那么容易发生循环调用。

观察者模式的应用场景
  • 广播链问题。一个对象其实既可以作为观察者也可以作为被观察者,这样消息像在广播链中传递,但是需要注意如果链的长度如果过程,就会变的复杂,难以控制。
  • 异步处理的问题。在异步处理过程需要考虑线程安全和队列的问题。







0 0
原创粉丝点击