设计模式之观察者模式

观察者模式的定义:是定义对象的一种一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖于它的对象都得到通知并被自动更新,在这里被观察者是“一".观察者是”多

组成:抽象被观察者类Observable(也成之为目标类),具体的被观察者类(extends Observable),抽象的观察者类(Observable),具体的观察者类 

在java里要实现观察者模式,就需要实现Observer接口,继承Observable类,其中观察者实现Observer接口,被观察者继承Observable类。其核心源码如下所示:

public class Observable {

    private boolean changed = false;
    private Vector obs;//持有一系列观察者

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

    /**
     * 添加观察者类
     * @param o 
     */
    public synchronized void addObserver(Observer o) {
        if (null == o) {
            throw new NullPointerException();
        }

        if (!obs.contains(o)) {
            this.obs.add(o);
        }
    }

   

    /**
     * 通知观察者
     * @param object 
     */
    public void notifyObservers(Object object) {
        Object[] observers;

        synchronized (this) {
            //判断是否执行更新操作
            if (!this.changed) {
                return;
            }

            observers = obs.toArray();
            this.clearChanged();
        }

        //所有观察者执行更新操作
        for (int i = observers.length - 1; i >= 0; i--) {
            Observer observer = (Observer) observers[i];
            observer.update(this, object);
        }
    }

}

 

public interface Observer {
    /**
     * 当被观察者的对象发生变化时,会调用该方法
     * @param o 被观察的对象
     * @param args 
     */
    void update(Observable o,Object args);
}


简单的一个举一个没有实际使用价值的例子:

考虑到如下场景:一个公司的股票(Share),有许多人投资者(Investor)购买,投资者们有一个共同的行为:
1、比较关注股票价格的变动
2、随着股票价格的变动每个投资者都作出自己的响应:卖出,继续持仓,买进等等行为

其实这种场景很适合使用观察者模式,至于为什么适合使用观察者模式,现在举个反例来从侧面说明观察者模式的优点:

不使用观察者模式的实现:

假设有三个投资者:InvestorA、InverstorB、InverstorC,具体实现类如下:
public class InvestorA {
    public void actionA(float price) {
        System.out.println("看这股票价格貌似还有上涨的空间,先不卖继续持有");
    }
}
public class InvestorB {
    public void actionB(float price) {
        System.out.println("内幕消息说这股票前景不乐观啊,卖了走人");
    }
}
public class InvestorC {
    public void doSomething(float price) {
        System.out.println("听王大妈说这股票可以,我也买一股");
    }
}
那么股票类如果价格发生变动想通知InvestorA、InverstorB、InverstorC价格发生变化,则必须持有这三个投资者对象的引用,其核心实现如下:
public class Share {
    //股票发行价格
    private float price = 1024f;

    //股东或者投资者A
    private InvestorA investorA;
    private InvestorB investorB;
    private InvestorC investorC;

    public void setPice(float newPrice) {
        if (this.price != newPrice) {
            price = newPrice;
            //通知投资者A价格变动
            investorA.actionA(newPrice);
            //通知投资者B价格变动
            investorB.actionB(newPrice);
            //通知投资者C价格变动
            investorC.doSomething(newPrice);
        }
    }

    public void setInvestorA(InvestorA investorA) {
        this.investorA = investorA;
    }

    public void setInvestorB(InvestorB investorB) {
        this.investorB = investorB;
    }

    public void setInvestorC(InvestorC investorC) {
        this.investorC = investorC;
    }

    public static void main(String args[]) {
        Share share = new Share();
        share.setInvestorA(new InvestorA());
        share.setInvestorB(new InvestorB());
        share.setInvestorC(new InvestorC());
        share.setPice(101f);
    }
}


简单的股票系统实现了,但是该类有如下问题:
1、股票类必须持有各个投资对象的引用,耦合度太高

2、如果再有投资人D、E、F。。。的话,则Share必须再添加D、E、F。。。的引用,在提供响应的set等方法,代码量会激增
扩展性不强。
3、看看setPrice 通知各个类,InvestorA对象执行了actionA()、InvestorB对象执行了actionB()、InvsertorC对象执行了doSomething方法,更恐怖的是后续的投资者可能是执行了methodx(),xx(),yy() 等等乱七八糟的方法(本来是一致的行为,但是执行的方法不一样,导致了代码的臃肿)。且Share对象必须知道这些不同的投资人的这些方法名。


上面三点说明了这是何其糟糕的设计,下面看看用观察者模式的思维实现的上述系统:

观察者模式的实现:
首先设计一个ShareObserver接口,该接口提供了doAction(float newPrice)方法,该接口方法的主要作用就是当股票价格发生改变的时候通知投资者,不同的投资者根据最新股价来做自己的处理,也就是说投资者需要实现该接口。

public interface ShareObserver {
    void doAction(float newPrice);
}
以InvestorA为例,作为一个股票的观察者,实现ShareObserver接口后的代码如下所示:
public class InvestorA implements ShareObserver {
    @Override
    public void doAction(float newPrice) {
        System.out.println("看这股票价格貌似还有上涨的空间,先不卖继续持有");
    }
}
股票类作为一个被观察者,其类经过修改后如下所示:
public class ShareObserable {
    private float price = 100f;
    //股票观察者集合类
    List<ShareObserver> shareObservers = new ArrayList<ShareObserver>();

    //添加股票投资者
    public void addInverstor(ShareObserver shareObserver) {
        shareObservers.add(shareObserver);
    }

    public static void main(String args[]) {
        ShareObserable share = new ShareObserable();
        share.addInverstor(new InvestorA());
        share.addInverstor(new InvestorB());
        share.addInverstor(new InvestorC());
        share.updatePrice(101f);
    }

    public void updatePrice(float newPrice) {
        if (this.price != newPrice) {
            price = newPrice;
            //通知投资者价格变动
            for (ShareObserver shareObserver : shareObservers) {
                shareObserver.doAction(newPrice);
            }
        }
    }
}

明显感觉到了观察者模式带来的好处:
1、所有观察者要实现固定的接口,这样从代码设计上来说,每个观察者行为得到统一,比如都会执行上述的doAction方法
2、当新的投资人InverstorXX需要观察该股票的时候,同样需要实现ShareObserver接口,然后将该对象通过ShareOb se 的addInverstor 添加到观察者集合中即可,可扩展性得到了加强。

上面的例子只是利用观察者思想来简单的模拟观察者的实现,当然客户端也可以通过实现java.util包里的Observer和Observerable接口来完成上面的股票系统,博主就偷个懒不实现了,感兴趣的读者可以自己是实现。


到此为止,还有个问题不知道读者感知到了没有,就是上面的观察者接口ShareObserver能力很有限,只能用于特定的例子(比如上面的股票系统),而不能做其他的场景。换句话说ShareObserver 做的还不够抽象,所以还需要改进。

在android中也经常看到观察者模式的影子,比如ListView,具体可参考博主的《 ListView观察者模式的应用

上面的观察者模式的代码结构如果用图表示的话,可以用如下图表示:
List<Observer>:

其实,我们也可以将各个观察者分组或者设立tag,形成结构如下的更为强大的观察者模式:
Map<group,List<Observer>>:



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

郭梧悠

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值