设计模式-观察者模式 (发布/订阅模型)

1、定义

Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically .

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

2、类图

3、角色说明

 Subject被观察者

定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。

Observer观察者

观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。

ConcreteSubject具体的被观察者

定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

ConcreteObserver具体的观察者

每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。


3、一个观察者例子

定义一个被观察者接口,实现这个接口的都是被观察者

/**
 * Created by ZLM on 2018/7/3.
 * Describe 被观察者接口,实现这个接口的都是被观察者
 */

public interface CommonObservable {
    //添加一个观察者,一个被观察者可以被多个观察者观察
    public void addObserver(CommonOberver observable);

    //删除一个观察者
    public void deleteObserver(CommonOberver observable);

    //通知观察者做出行动
    public void notifyObervers(String str);

}

当然被观察者也有自己特有的方法

/**
 * Created by ZLM on 2018/7/3.
 * Describe 被观察者自身特有功能
 */

public interface ICustomObservable {
    //被观察的方法
    public void specialMeyhod();
}

定义一个观察者接口,实现该接口的都是观察者

/**
 * Created by ZLM on 2018/7/3.
 * Describe 观察者接口
 */

public interface CommonOberver {
    //一旦发现有情况,就做出行动
    public void doSomething(String str);
}

定义一个被观察者

/**
 * Created by ZLM on 2018/7/3.
 * Describe 定义一个被观察者
 */

public class ANewOberverable implements ICustomObservable, CommonObservable {

    //存放所有的观察者
    private ArrayList<CommonOberver> mObervers = new ArrayList<>();

    //观察者要活动了,也就是我们需要观察的方法
    @Override
    public void specialMeyhod() {
        this.notifyObervers("被观察者 : \t" + this.getClass().getSimpleName() + "\t活动了!!!请注意");
    }

    //增加观察者
    @Override
    public void addObserver(CommonOberver observer) {
        this.mObervers.add(observer);
    }

    //删除观察者
    @Override
    public void deleteObserver(CommonOberver observer) {
        this.mObervers.remove(observer);
    }

    //通知观察者
    @Override
    public void notifyObervers(String str) {
        for (CommonOberver oberver : mObervers) {
            oberver.doSomething(str);
        }
    }
}

定义2个观察者

/**
 * Created by ZLM on 2018/7/3.
 * Describe 观察者A
 */

public class OberverA implements CommonOberver {
    @Override
    public void doSomething(String str) {
        System.out.println("观察者:\t" + this.getClass().getSimpleName() + "\t收到信息:" + str);
    }
}
/**
 * Created by ZLM on 2018/7/3.
 * Describe 观察者B
 */

public class OberverB implements CommonOberver {
    @Override
    public void doSomething(String str) {
        System.out.println("观察者:\t" + this.getClass().getSimpleName() + "\t收到信息:" + str);
    }
}

调用:

OberverA oberverA = new OberverA();
OberverB oberverB = new OberverB();
ANewOberverable aNewOberverable = new ANewOberverable();
aNewOberverable.addObserver(oberverA);
aNewOberverable.addObserver(oberverB);
aNewOberverable.specialMeyhod();

输出:

I/System.out: 观察者:	OberverA	收到信息:被观察者 : 	ANewOberverable	活动了!!!请注意
I/System.out: 观察者:	OberverB	收到信息:被观察者 : 	ANewOberverable	活动了!!!请注意

4、观察者模式的优缺点

优点:

--观察者和被观察者之间是抽象耦合。不管观察者还是被观察者都非常容易扩展。而且在Java中都已经实现的抽象层级的定义,在系统方面更是得心应手。

--建立一套触发机制。

缺点:

--观察者模式需要考虑一下开发效率和运行效率问题,一个被观察者,多个观察者,开发 和调试就会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。多级触发时的效率更是让人担忧,大家在设计时注意考虑。

5、使用场景

--关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。

--事件多级触发场景。

-- 跨系统的消息交换场景,如消息队列的处理机制。

6、注意

--广播链问题。根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),这还是比较好控制的。

--它和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。

--异步处理问题。被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的 问题。

7、扩展

其实,Java中已经给我们提供了可扩展的 观察者(java.util.Observer) 和 被观察者(java.util.Observable父类。替换掉上文提到的commonObservable和commonObserve

/**
 * Created by ZLM on 2018/7/3.
 * Describe 定义一个被观察者
 */

public class ANewOberverable extends Observable implements ICustomObservable {

    //观察者要活动了,也就是我们需要观察的方法
    @Override
    public void specialMeyhod() {
        super.setChanged();
        super.notifyObservers("被观察者:" + this.getClass().getSimpleName() + "活动了!!!请注意");

    }
}
/**
 * Created by ZLM on 2018/7/3.
 * Describe 被观察者自身特有功能
 */

public interface ICustomObservable {
    //被观察的方法
    public void specialMeyhod();
}
/**
 * Created by ZLM on 2018/7/3.
 * Describe 观察者
 */

public class OberverA implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("观察者:" + this.getClass().getSimpleName() + "收到信息" + arg.toString());
    }
}
/**
 * Created by ZLM on 2018/7/3.
 * Describe 观察者
 */

public class OberverB implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("观察者:" + this.getClass().getSimpleName() + "收到信息" + arg.toString());
    }
}

调用:

OberverA oberverA = new OberverA();
OberverB oberverB = new OberverB();
ANewOberverable aNewOberverable = new ANewOberverable();
aNewOberverable.addObserver(oberverA);
aNewOberverable.addObserver(oberverB);
aNewOberverable.specialMeyhod();

输出:

I/System.out: 观察者:OberverA收到信息被观察者:ANewOberverable活动了!!!请注意
I/System.out: 观察者:OberverB收到信息被观察者:ANewOberverable活动了!!!请注意

8、真实项目中如何使用

-- 观察者和被观察者之间的消息沟通

被观察者状态改变会触发观察者的一个行为,同时会传递一个消息给观察者,这是正确 的,在实际中一般的做法是:观察者中的update方法接受两个参数,一个是被观察者,一个 是DTO(Data Transfer Object,据传输对象),DTO一般是一个纯洁的JavaBean,由被观察者生成,由观察者消费。当然,如果考虑到远程传输,一般消息是以XML格式传递。

--观察者响应方式

我们这样来想一个问题,观察者是一个比较复杂的逻辑,它要接受被观察者传递过来的信息,同时还要对他们进行逻辑处理,在一个观察者多个被观察者的情况下,性能就需要提到日程上来考虑了,为什么呢?如果观察者来不及响应,被观察者的执行时间是不是也会被拉长?那现在的问题就是:观察者如何快速响应?有两个办法:

        一是采用多线程技术,甭管是被观察者启动线程还是观察者启动线程,都可以明显地提高系统性能,这也就是大家通常所说的异步架构;

        二是缓存技术,甭管你谁来,我已经准备了足够的资源给你了,我保证快速响应,这当然也是一种比较好方案,代价就是开发难度很大,而且压力测试要做的足够充分,这种方案也就是大家说的同步架构。

--被观察者尽量自己做主

被观察者的状态改变是否一定要通知观察者呢?不一定吧,在设计的时候要灵活考虑,否则会加重观察者的处理逻辑,一般是这样做的,对被观察者的业务逻辑 doSomething方法实现重载,如增加一个doSomething(boolean isNotifyObs)方法,决定是否通知观察者,而不是在消息到达观察者时才判断是否要消费。



欢迎关注微信公众号:OpenShare,共同成长,共同进步:














猜你喜欢

转载自blog.csdn.net/aminy123/article/details/80887000