设计模式————观察者模式

定义

观察者(Observer)模式的定义:是种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式。

角色

在观察者模式中有以下角色:

Subject:抽象主题(抽象被观察者,是具体被观察者的抽象类),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
ConcreteSubject:具体主题(具体被观察者,实现抽象被观察者定义的接口),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知
Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题状态更改通知时更新自己
ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题状态更改通知时更新自身的状态。

拿微信公众号来举例,假设微信用户就是观察者微信公众号是被观察者,可以有多个微信用户关注一个微信公众号,当这个微信公众号更新时就会通知这些订阅的微信用户。

简单实例

抽象观察者(Observer)
public interface Observer {
    public void update(String message);		# 定义更新方法
}
具体观察者(ConcrereObserver):即微信用户
public class WeixinUser implements Observer {
    // 微信用户名
    private String name;
    
    public WeixinUser(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String message) {
        System.out.println(name + "-" + message);
    }
    
}
抽象被观察者(Subject)
public interface Subject {
    //增加订阅者
    public void attach(Observer observer);
    
    //删除订阅者
    public void detach(Observer observer);
    
    //通知订阅者更新消息
    public void notify(String message);
}
具体被观察者(ConcreteSubject):即微信公众号
public class SubscriptionSubject implements Subject {
    //储存订阅公众号的微信用户
    private List<Observer> weixinUserlist = new ArrayList<Observer>();

    @Override
    public void attach(Observer observer) {
        weixinUserlist.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        weixinUserlist.remove(observer);
    }

    @Override
    public void notify(String message) {
        for (Observer observer : weixinUserlist) {
            observer.update(message);
        }
    }
}

测试:

public class Client {
    public static void main(String[] args) {
    	//创建微信公众号
        SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
        //创建微信用户
        WeixinUser user1=new WeixinUser("杨影枫");
        WeixinUser user2=new WeixinUser("月眉儿");
        WeixinUser user3=new WeixinUser("紫轩");
        //订阅公众号
        mSubscriptionSubject.attach(user1);
        mSubscriptionSubject.attach(user2);
        mSubscriptionSubject.attach(user3);
        //公众号更新发出消息给订阅的微信用户
        mSubscriptionSubject.notify("刘望舒的专栏更新了");
    }
}

运行结果:

杨影枫-刘望舒的专栏更新了
月眉儿-刘望舒的专栏更新了
紫轩-刘望舒的专栏更新了

应用

android源码中也有很多使用了观察者模式,比如 OnClickListener、ContentObserver、android.database.Observable等;还有组件通讯库RxJava、RxAndroid、EventBus

观察者模式在软件开发中用得最多的是窗体程序设计中的事件处理,窗体中的所有组件都是“事件源”,也就是目标对象,而事件处理程序类的对象是具体观察者对象。下面以一个学校铃声的事件处理程序为例,介绍 Windows 中的“事件处理模型”的工作原理。

例1:在本实例中,学校的“铃”是事件源和目标(具体被观察者),“老师”和“学生”是事件监听器和具体观察者,“铃声”是事件类。学生和老师来到学校的教学区,都会注意学校的铃,这叫事件绑定;当上课时间或下课时间到,会触发铃发声,这时会生成“铃声”事件;学生和老师听到铃声会开始上课或下课,这叫事件处理。这个实例非常适合用观察者模式实现,下图给出了学校铃声的事件模型。
在这里插入图片描述

代码结构:

  • 铃声事件(RingEvent)类,它记录了铃声的类型(上课铃声/下课铃声);
  • 铃(BellEventSource)类,它是事件源,是具体被观察者。该类里面包含了监听器容器 listener,可以绑定监听者(学生或老师),并且有产生铃声事件和通知所有监听者的方法;
  • 铃声事件监听者(BellEventListener)类,它是抽象观察者,它包含了铃声事件处理方法 heardBell(RingEvent e);
  • 老师类(TeachEventListener)和学生类(StuEventListener),它们是事件监听器,是具体观察者,听到铃声会去上课或下课
package observer;
import java.util.*;
public class BellEventTest
{
    public static void main(String[] args)
    {
        BellEventSource bell=new BellEventSource(); //铃(事件源)    
        bell.addPersonListener(new TeachEventListener()); //注册监听器(老师)
        bell.addPersonListener(new StuEventListener()); //注册监听器(学生)
        bell.ring(true); //打上课铃声
        System.out.println("------------");
        bell.ring(false); //打下课铃声
    }
}
//铃声事件类:用于封装事件源及一些与事件相关的参数
class RingEvent extends EventObject
{
    private static final long serialVersionUID=1L;
    private boolean sound; //true表示上课铃声,false表示下课铃声
    public RingEvent(Object source,boolean sound)
    {
        super(source);
        this.sound=sound;
    }
    public void setSound(boolean sound)
    {
        this.sound=sound;
    }
    public boolean getSound()
    {
        return this.sound;
    }
}
//目标类:事件源,铃
class BellEventSource
{
    private List<BellEventListener> listener; //监听器容器
    public BellEventSource()
    {
        listener=new ArrayList<BellEventListener>();
    }
    //给事件源绑定监听器 
    public void addPersonListener(BellEventListener ren)
    {
        listener.add(ren);
    }
    //事件触发器:敲钟,当铃声sound的值发生变化时,触发事件。
    public void ring(boolean sound)
    {
        String type=sound?"上课铃":"下课铃";
        System.out.println(type+"响!");
        RingEvent event=new RingEvent(this, sound);
        notifies(event); //通知注册在该事件源上的所有监听器                
    }
    //当事件发生时,通知绑定在该事件源上的所有监听器做出反应(调用事件处理方法)
    protected void notifies(RingEvent e)
    {
        BellEventListener ren=null;
        Iterator<BellEventListener> iterator=listener.iterator();
        while(iterator.hasNext())
        {
            ren=iterator.next();
            ren.heardBell(e);
        }
    }
}
//抽象观察者类:铃声事件监听器
interface  BellEventListener extends EventListener
{
    //事件处理方法,听到铃声
    public void heardBell(RingEvent e);
}
//具体观察者类:老师事件监听器
class TeachEventListener implements BellEventListener
{
    public void heardBell(RingEvent e)
    {
        if(e.getSound())
        {
            System.out.println("老师上课了...");
        }
        else
        {
            System.out.println("老师下课了...");
        }
    }
}
//具体观察者类:学生事件监听器
class StuEventListener implements BellEventListener
{
    public void heardBell(RingEvent e)
    {
        if(e.getSound())
        {
            System.out.println("同学们,上课了...");
        }
        else
        {
            System.out.println("同学们,下课了...");
        }
    }
}

运行结果:

上课铃响!
老师上课了...
同学们,上课了...
------------
下课铃响!
老师下课了...
同学们,下课了...

例1总结:事件源负责绑定与解绑事件监听者,并生成具体事件(可以有多种事件,每种事件都是一个类)然后通知事件监听者(调用事件监听者对该事件的处理函数)。

具体接口

在Java中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

  1. Observable类

    Observable 类是抽象目标类,它有一个 Vector 向量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法。

    void addObserver(Observer o) 方法:用于将新的观察者对象添加到向量中。

    void notifyObservers(Object arg) 方法:调用向量中的所有观察者对象的 update 方法,通知它们数据发生改变。通常越晚加入向量的观察者越先得到通知。
    void setChange() 方法:用来设置一个 boolean 类型的内部标志位,注明目标对象发生了变化。当它为真时,notifyObservers() 才会通知观察者。

  2. Observer 接口
    Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 void update(Observable o,Object arg) 方法,进行相应的工作。

例2:利用 Observable 类和 Observer 接口实现原油期货的观察者模式实例。

分析:当原油价格上涨时,空方伤心,多方局兴;当油价下跌时,空方局兴,多方伤心。本实例中的抽象目标(Observable)类在 Java 中已经定义,可以直接定义其子类,即原油期货(OilFutures)类,它是具体目标类,该类中定义一个 SetPriCe(float price) 方法,当原油数据发生变化时调用其父类的 notifyObservers(Object arg) 方法来通知所有观察者;另外,本实例中的抽象观察者接口(Observer)在 Java 中已经定义,只要定义其子类,即具体观察者类(包括多方类 Bull 和空方类 Bear),并实现 update(Observable o,Object arg) 方法即可。

package observer;
import java.util.Observer;
import java.util.Observable;
public class CrudeOilFutures
{
    public static void main(String[] args)
    {
        OilFutures oil=new OilFutures();
        Observer bull=new Bull(); //多方
        Observer bear=new Bear(); //空方
        oil.addObserver(bull);
        oil.addObserver(bear);
        oil.setPrice(10);
        oil.setPrice(-8);
    }
}
//具体目标类:原油期货
class OilFutures extends Observable
{
    private float price;
    public float getPrice()
    {
        return this.price;
    }
    public void setPrice(float price)
    {
        super.setChanged() ; //设置内部标志位,注明数据发生变化 
        super.notifyObservers(price); //通知观察者价格改变了 
        this.price=price ;
    }
}
//具体观察者类:多方
class Bull implements Observer
{
    public void update(Observable o,Object arg)
    {
        Float price=((Float)arg).floatValue();
        if(price>0)
        {
            System.out.println("油价上涨"+price+"元,多方高兴了!");
        }
        else
        {
            System.out.println("油价下跌"+(-price)+"元,多方伤心了!");
        }
    }
}
//具体观察者类:空方
class Bear implements Observer
{
    public void update(Observable o,Object arg)
    {
        Float price=((Float)arg).floatValue();
        if(price>0)
        {
            System.out.println("油价上涨"+price+"元,空方伤心了!");
        }
        else
        {
            System.out.println("油价下跌"+(-price)+"元,空方高兴了!");
        }
    }
}

运行结果:

油价上涨10.0元,空方伤心了!
油价上涨10.0元,多方高兴了!
油价下跌8.0元,空方高兴了!
油价下跌8.0元,多方伤心了!

总结

被观察者内部保存了所有的观察者对象(因此被观察者可以调用观察者的方法),被观察者三个主要方法分别是增加观察者、删除观察者、通知观察者(又可以进一步筛选,是全部通知还是部分通知)

优点:

  • 降低了被观察者与观察者之间的耦合关系,两者之间是抽象耦合关系
  • 被观察者与观察者之间建立了一套触发机制(即被观察者发生改变,将触发观察者发生改变)

缺点:

  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率(尤其是当一个观察者的通知卡顿时,会影响剩下的通知,可以用异步通知解决)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值