浅谈设计模式-观察者模式

书接上回,本篇讲一下行为型模式-观察者模式

观察者模式

定义:定义了对象之间的一对多依赖关系,让多个依赖对象(观察者)同时监听某一个主题对象(被观察者/目标对象),当主题对象状态发生变化时,它的所有依赖对象都会收到通知并自动更新。

UML图

Subject:目标对象,也叫被观察对象,观察者模式中属于一方。它一般有以下几个功能

        1> 一个目标可以别多个观察者观察

        2>目标提供对观察者添加与删除的维护

        3>当状态发送变化时,需要通知所有观察者。

/**
 * 目标对象(被观察者)
 */
public class Subject {
    //观察者(订阅者)集合
    private List<Observer> observerList = new ArrayList<>();


    //添加观察者(订阅者)
    public void attach(Observer observer){
        observerList.add(observer);
    }

    //删除观察者(订阅者)
    public void detach(Observer observer){
        observerList.remove(observer);
    }


    //通知所有观察者(订阅者),并回调它们暴露update方法,实现同步更新
    public void notifyObservers(){
        for (Observer observer : observerList) {
            observer.update(this);
        }
    }
}

Observer:一个接口,成为观察者,观察者模式中属于多方。它一般需要实现以下功能

       1>定义一个update方法,方法参数为目标对象,当目标对象触发通知时,调用。

/**
 * 观察者(订阅者)
 */
public interface Observer {
    /**
     * 当目标对象状态变化后,该方法会被调用,
     * 目的:同步更新观察者(订阅者)状态
     * @param subject
     */
    void update(Subject subject);
}

ConcreteSubject:具体的目标实现对象,里面会维护目标状态,当状态改变时,通知所有观察者。

public class ConcreteSubject  extends  Subject{

    private String state = "A状态";

    public void  changeState(String state){
        System.out.println("目标对象state状态发生变动了,由原来:"+ this.state + ",改为:" + state);
        this.state = state;

        //状态变动后通知所有观察者(订阅者)
        notifyObservers();
    }

    public String getState() {
        return state;
    }
}

ConcreteObserver:观察者的具体实现对象,用来接收目标的通知,并执行预设的业务逻辑。

//具体观察者(订阅者)
public class ConcreteObserver implements Observer{
    @Override
    public void update(Subject subject) {
        System.out.println("观察到目标对象(被观察者:subject)的状态变化了....");

        ConcreteSubject sb = (ConcreteSubject) subject;
        System.out.println("最新状态为:" + sb.getState());

    }
}

App:测试类

public class App {
    public static void main(String[] args) {
        //观察者(订阅者)
        ConcreteObserver ob = new ConcreteObserver();

        //目标对象(被观察者)
        ConcreteSubject sb = new ConcreteSubject();
        sb.attach(ob);
        sb.changeState("B状态");
    }
}

结果:

目标对象state状态发生变动了,由原来:A状态,改为:B状态
观察到目标对象(被观察者:subject)的状态变化了....
最新状态为:B状态

案例分析

需求:商品到货通知

描述:IPhone20 出来的,N多果粉想订购,应为太火了,供不应求,官网提供到货通知机制,小明跟小红关注了。希望到货之后能第一时间收到消息。

UML图 

Subject:目标对象-商品

定义目标对象,包含3个方法

attach:添加所有观察者(订阅者)

detach:删除退订的观察者(订阅者)

notifyObservers:当状态变化时,通知所有观察者(订阅者)

/**
 * 目标对象(被观察者):商品
 */
public class Subject {
    //观察者(订阅者)集合
    private List<Observer> observerList = new ArrayList<>();

    //添加观察者(订阅者)
    public void attach(Observer observer){
        observerList.add(observer);
    }

    //删除观察者(订阅者)
    public void detach(Observer observer){
        observerList.remove(observer);
    }


    //通知所有观察者(订阅者),并回调它们暴露update方法,实现同步更新
    public void notifyObservers(){
        for (Observer observer : observerList) {
            observer.update(this);
        }
    }
}

ProductObserver :具体商品 

定义真实的目标对象,上面Subject类 定义目标对象基本操作,ProductObserver 扩展具体的状态变化逻辑。

package com.langfeiyes.pattern.observer.demo2;

//目标对象:商品
public class ProductObserver extends Subject{

    private String name;
    private boolean state = false;  //约定:false:缺货,true:有货

    public ProductObserver(String name){
        this.name = name;
    }

    //进货
    public void stock(int count){
        System.out.println("商家最新进货,上架商品:" +this.name+ ",共" + count +" 部");
        state = true;
        notifyObservers();
    }

    public boolean getState(){
        return state;
    }

    public String getName() {
        return name;
    }
}

Observer:观察者-消费者 

定义观察者实现操作规则,只有接口方法。

/**
 * 观察者(订阅者):消费者
 */
public interface Observer {
    /**
     * 当目标对象状态变化后,该方法会被调用,
     * 目的:同步更新观察者(订阅者)状态
     * @param subject
     */
    void update(Subject subject);
}

 ConsumerObserver:具体消费者

定义具体订阅者,实现update方法,update方法中定制当目标对象发生变化时,订阅者如何同步更新自身状态(具体的业务逻辑)

//观察者:消费者
public class ConsumerObserver  implements Observer{

    private String name;

    public ConsumerObserver(String name){
        this.name = name;
    }
    @Override
    public void update(Subject subject) {
        ProductObserver po = (ProductObserver) subject;
        if(po.getState()){
            System.out.println(this.name + ":心念念的"+po.getName()+",终于到啦,抢呀~");
        }else{
            System.out.println(this.name + "小明:还没到啊,花都谢了~");
        }

    }
}

App:测试类

public class App {
    public static void main(String[] args) {
        //目标对象
        ProductObserver ob = new ProductObserver("Iphone20");

        //观察者
        ConsumerObserver xm = new ConsumerObserver("小明");
        ConsumerObserver xh = new ConsumerObserver("小红");

        //小明小红订阅:到货通知
        ob.attach(xm);
        ob.attach(xh);

        //商品进货
        ob.stock(100);
    }
}

结果:

商家最新进货,上架商品:Iphone20,共100 部
小明:心念念的Iphone20,终于到啦,抢呀~
小红:心念念的Iphone20,终于到啦,抢呀~

适用场景

关联行为场景,建立一套触发机制

比如:各种事件触发,各种通知,各种推送,都可以使用观察者模式

优缺点

优点

观察者和被观察者之间建立抽象的耦合
观察者模式实现动态联动
观察者模式支持广播通信

缺点

可能会引起无畏的操作
观察者之间有过多的细节依赖,提高时间消耗及程序复杂度
使用要得当,要避免循环调用

实现方式

在实现观察模式实现中,具体分2种情况:推模型和拉模型

推模型

目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通常是目标对象的全部或部分数据。此为被动型

拉模型

目标对象在通知观察者信息时,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于观察者从目标对象中拉数据。此为主动型

开发案例

还是看回JDK,JDK中其实有个现成的观察模式

Observable :可观察的(即被观察者/目标对象)

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }
    public void notifyObservers(Object arg) {
            synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }
    protected synchronized void setChanged() {
        changed = true;
    }
}

Observer : 观察者

public interface Observer {
    void update(Observable o, Object arg);
}

代码跟上面我们写的逻辑一样,区别:就是JDK为线程安全的,使用Vector缓存观察者,使用synchronized 限制添加,删除,清空观察者,保证原子操作。

下面是使用JDK方式,重写商品订购案例, 只需要2个方法即可。

ConsumerObserver 

//观察者:消费者
public class ConsumerObserver implements Observer {
    private String name;
    public ConsumerObserver(String name){
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        ProductObserver po = (ProductObserver) o;
        if(po.hasChanged()){
            System.out.println(this.name + ":心念念的"+po.getName()+",终于到啦,抢呀~");
        }else{
            System.out.println(this.name + "小明:还没到啊,花都谢了~");
        }
    }
}

 ProductObserver 

//目标对象:商品
public class ProductObserver extends Observable {
    private String name;
    public ProductObserver(String name){
        this.name = name;
    }
    //进货
    public void stock(int count){
        System.out.println("商家最新进货,上架商品:" +this.name+ ",共" + count +" 部");
        super.setChanged(); //改变状态
        notifyObservers();  //发起通知
    }
    public String getName() {
        return name;
    }
}

总结

观察者模式的本质:触发联动

1>当目标对象状态变动时,触发通知,所有关注该对象的观察者都可以联动同步更新

2>观察者联动同步更新可以通过注册与取消来控制观察者

3>另外目标对象和观察者对象之间是解耦,可以保证观察者无论发送啥变化,目标对象都能联动起来

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪飞yes

我对钱没兴趣~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值