设计模式——观察者模式

转自:https://www.cnblogs.com/jenkinschan/p/5677552.html

概述

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式有时成为发布/订阅模式,就是让多个对象在一个对象的状态改变时被通知到。

解决问题

当一个系统有多个类协同工作,如果在一个类中需要知道另外一个类的实现细节才能让系统运转,就会导致系统耦合过紧,以后相互依赖的类改变了或者新增了依赖的类,很多类需要同时更改。为了让交互对象之间能够松耦合,我们可以使用观察者模式。

结构类图

这里写图片描述

成员角色

抽象主题(Subject):把所有对观察者对象的引用保存到一个集合中,每个抽象主题角色都有任何数量的观察者。抽象主题角色提供一个接口,可以增加和删除观察者。一般用一个抽象类和接口实现。
抽象观察者(Observer):为所有具体观察者定义一个接口,在得到主题的通知时更新自己。
具体主题(ConcreteSubject):继承抽象主题,在具体主题内部状态改变时,给所有登记过的观察者发出通知。
具体观察者(ConcreteObserver):实现抽象观察者所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者可以保存一个指向具体主题的引用。

应用实例

下面是银行账号资金变动下发通知的例子,只要账号资金变动,就会同时下发app客户端消息、邮件消息、短信消息通知客户账号资金变动了

主题接口

public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}

抽象主题

abstract public class AbstractSubject implements Subject{
    //可以在这里新增Subject没有的方法,以便子类共用
}

观察者接口

public interface Observer {
    public void update(double money);
}

app客户端观察者

public class APPObserver implements Observer{
    public void update(double money){
        //发送app客户端通知账号资金变动
        System.out.println("app客户端提醒:您的账户资金变动:" + money);
    }
}

邮件观察者

public class MailObserver implements Observer{
    public void update(double money){
        //发送邮件通知账号资金变动
        System.out.println("邮件提醒:您的账户资金变动:" + money);
    }
}

短信观察者

public class SmsObserver implements Observer{
    public void update(double money){
        //发送短信通知账号资金变动
        System.out.println("短信提醒:您的账户资金变动:" + money);
    }
}

客户银行账号主题

public class AccountSubject extends AbstractSubject{
    //观察者集合
    private List<Observer> observers;
    //资金变动
    private double money;

    public AccountSubject(){
        //主题实例化时,初始化观察者集合
        observers = new ArrayList<Observer>();
    }

    //账号资金变动时通知观察者
    public void notifyObservers() {
        if(observers.size() > 0){
            for(Observer observer:observers){
                observer.update(money);
            }
        }

    }

    //注册观察者
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    //删除观察者
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if(i > 0){
            observers.remove(i);
        }

    }

    //改变账户资金
    public void changeAccount(double money){       
        System.out.println("账户资金变动:" + money);
        this.money = money;

        //通知观察者
        notifyObservers();
    }

}

测试观察者

public class TestObserver {
    public static void main(String[] args){
        //创建账号主题
        AccountSubject subject = new AccountSubject();

        //创建观察者
        Observer appObserver = new APPObserver();
        Observer smsObserver = new SmsObserver();
        Observer mailObserver = new MailObserver();

        //注册观察者到账号主题
        subject.registerObserver(appObserver);
        subject.registerObserver(mailObserver);
        subject.registerObserver(smsObserver); 

        subject.changeAccount(7.8);
    }
}

测试结果:
这里写图片描述

java内置观察者模式

其实 Java内置了观察者模式,我们可以直接使用java.util包中的Obserber接口和Observable类实现观察者模式。下面我们把上面的代码稍微修改,用Java内置的观察者模式实现银行账号资金变动下发各种通知。

java内置的观察者接口

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

    public Observable() {
        obs = new Vector<>();
    }

    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) {
        Object[] arrLocal;

        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;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return obs.size();
    }
}

主题对象

import java.util.Observable;

public class AccountSubject extends Observable{

    //资金变动数额
    private double money;

    public AccountSubject(){
    }

    //改变账户资金
    public void changeAccount(double money){       
        System.out.println("(测试java自带观察者模式)账户资金变动:" + money);
        this.money = money;

        //被观察者状态改变
        this.setChanged();
        //java内部实现推拉通知,使用时直接调用notifyObservers(),
        //或者notifyObservers(Object o),传递的参数会在观察者类接收到
        notifyObservers();
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }  

}

java内置观察者接口

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

app客户端观察者

import java.util.Observable;
import java.util.Observer;

public class APPObserver implements Observer{
    //在观察者类图中我们看到,观察者可以持用被观察者的引用
    Observable observale;

    public APPObserver(Observable observable){
        this.observale = observable;
        //直接调用java自带的方法,把观察者注册到被观察者
        observable.addObserver(this);
    }

    //具体观察者实现Observer接口的方法,
    //arg1参数就是被观察者的notifyObservers(Object o)传过来的参数
    public void update(Observable arg0, Object arg1) {
        if(arg0 instanceof AccountSubject){
            AccountSubject accountSubject = (AccountSubject)arg0;
            //发送app客户端通知账号资金变动
            System.out.println("app客户端提醒:您的账户资金变动:" + accountSubject.getMoney());
        }

    }
}

邮件观察者

import java.util.Observable;
import java.util.Observer;

public class MailObserver implements Observer{

    //在观察者类图中我们看到,观察者可以持用被观察者的引用
    Observable observale;

    public MailObserver(Observable observable){
        this.observale = observable;
        //直接调用java自带的方法,把观察者注册到被观察者
        observable.addObserver(this);
    }

    //java的观察者有自带的update方法
    public void update(Observable arg0, Object arg1) {
        if(arg0 instanceof AccountSubject){
            AccountSubject accountSubject = (AccountSubject)arg0;
            //发送app客户端通知账号资金变动
            System.out.println("邮件提醒:您的账户资金变动:" + + accountSubject.getMoney());
        }
    }

}

短信观察者

import java.util.Observable;
import java.util.Observer;

public class SmsObserver implements Observer{

    //在观察者类图中我们看到,观察者可以持用被观察者的引用
    Observable observale;

    public SmsObserver(Observable observable){
        this.observale = observable;
        //直接调用java自带的方法,把观察者注册到被观察者
        observable.addObserver(this);
    }

    //java的观察者有自带的update方法
    public void update(Observable arg0, Object arg1) {
        if(arg0 instanceof AccountSubject){
            AccountSubject accountSubject = (AccountSubject)arg0;
            //发送app客户端通知账号资金变动
            System.out.println("短信提醒:您的账户资金变动:" + + accountSubject.getMoney());
        }          
    }
}

测试java自带观察者模式

import java.util.Observer;

public class TestObserver {
    public static void main(String[] args){
        //创建被观察者,也就是账号主题
        AccountSubject observble = new AccountSubject();

        //创建观察者
        Observer appObserver = new APPObserver(observble);
        Observer smsObserver = new SmsObserver(observble);
        Observer mailObserver = new MailObserver(observble);

        //账号资金变动
        observble.changeAccount(7.8);
    }
}

测试结果:
这里写图片描述

优点与缺点

1、优点
(1) 观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖抽象,而不是依赖具体。从而使各自的变化都不会影响到另一方。主题并不知道任何一个具体的观察者,只知道他们有一个共同的接口。
(2) 观察者模式支持“广播通信”。主题会向所有的观察者发出通知。
(3) 观察者模式符合“对修改关闭,对扩展开放”的设计原则。
2、缺点
(1) 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会话费很多时间。
(2) 观察者只知道被观察者发生了变化,但并不知道被观察者是如何发生变化的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值