美文网首页
设计模式之观察者模式

设计模式之观察者模式

作者: sssssss_ | 来源:发表于2018-11-17 15:17 被阅读0次

    一、观察者模式的定义

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

    二、观察者模式的使用场景

    1. 一个抽象模型有两个方面,其中一个方面依赖于另一个方面;(比如:View依赖于Model的数据,当数据发生改变时,更新View)
    2. 一个对象的改变将导致一个或多个其他对象也发生改变;(比如:同一个数据源对多个观察对象)
    3. 需要在系统创建一个触发链。(比如A影响B,B影响C)

    三、UML结构图

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

    四、实现1(自己写Observer和Observable)

    /*
     * 抽象被观察者,添加删除通知观察者。
     */
    public abstract class Subject {
        //保存注册观察者对象
        private Vector<Observer> mObservers = new Vector<>();
        //注册观察者对象
        public void addObserver(Observer o) {
            this.mObservers.add(o);
        }
        //注销观察者对象
        public void removeObserver(Observer o) {
            this.mObservers.remove(o);
        }
        //通知观察者对象
        public void notifyObservers() {
            for (Observer o : this.mObservers) {
                o.update();
            }
        }
    }
    /*
     * 具体被观察者
     */
    public class ConcreteSubject extends Subject {
        public void doSomething() {
            System.out.println("doSomething");
            this.notifyObservers();
        }
    }
    /*
     * 抽象观察者
     */
    public interface Observer {
        void update();
    }
    /*
     * 具体观察者
     */
    public class ConcreteObserver implements Observer {
        @Override
        public void update() {
            System.out.println("收到消息");
        }
    }
    /*
     * 测试类
     */
    public class Client {
        public static void main(String[] args) {
            ConcreteSubject cs = new ConcreteSubject();
            Observer observer1 = new ConcreteObserver();
            Observer observer2 = new ConcreteObserver();
            cs.addObserver(observer1);
            cs.addObserver(observer2);
            cs.doSomething();
        }
    }
    
    • 显示结果:
    doSomething
    收到消息
    收到消息
    

    五、实现2(实际开发中一般是使用java.util包下面的Observer、Observable)

    /*
     * 观察者
     */
    class EmailUser implements Observer {
        public String name;
        public EmailUser(String name) {
            this.name = name;
        }
        @Override
        public void update(Observable o, Object arg) {
            System.out.println("亲" + name + "," +
                    "你关注的栏目《" + arg + "》已经更新,请及时查看!");
        }
    }
    /*
     *被观察者
     */
    class Emailcolumn extends Observable {
        public void postNewColumn(String content) {
            setChanged();
            notifyObservers(content);
        }
    }
    /*
     * 测试代码
     */
    public class ObserverDemo {
        public static void main(String[] args) {
            Emailcolumn emailcolumn = new Emailcolumn();
            EmailUser user1 = new EmailUser("user1");
            EmailUser user2 = new EmailUser("user2");
            emailcolumn.addObserver(user1);
            emailcolumn.addObserver(user2);
            emailcolumn.postNewColumn("今天最开心:雷佳音");
        }
    }
    
    • 显示结果:
    亲user2,你关注的栏目《今天最开心:雷佳音》已经更新,请及时查看!
    亲user1,你关注的栏目《今天最开心:雷佳音》已经更新,请及时查看!
    

    六、观察者模式在Android中的实际运用

    1. 回调函数:一对一的关系
    2. ListView数据更新
    3. RxJava
    4. 广播注册
    5. EventBus

    1.回调函数

    定义:实现了抽象类/接口的实例,实现了父类提供的抽象方法后,将该方法交还给父类来处理。

    public class Employee {
        //定义回调接口的成员变量
        private Callback mcallback;
        //声明回调接口
        public interface Callback {
            public abstract void work();
        }
        //设置回调接口对象成员变量
        public void setCallback(Callback callback) {
            this.mcallback = callback;
        }
        //调用回调接口对象中的方法
        public void doWork() {
            if(mcallback!=null){
                mcallback.work();
            }
        }
    }
    public class Boss {
        private Employee employee;
        //为Employee设置回调函数,在这里定义具体的回调方法
        public void setCallback() {
            employee.setCallback(new Employee.Callback() {
                @Override
                public void work() {
                    System.out.println("work");
                }
            });
        }
    }
    

    再来看另一个回调的例子:

    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // do something
        }
    });
    

    OnClickListener作为观察者,button作为被观察者,通过setOnClickListener来形成订阅关系。当button被点击时相当于被观察者数据发生改变,就会去通知观察者OnClickListener,这时触发onClick(回调onClick)。

    • 面试题:回调函数和观察者模式的区别?
    • 观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。观察者模式完美的将观察者和被观察的对象分离开,一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
    • 回调函数:其实也算是一种观察者模式的实现方式,回调函数实现的观察者和被观察者往往是一对一的依赖关系。
    • 所以最明显的区别是观察者模式是一种设计思路,而回调函数式一种具体的实现方式;另一明显区别是一对多还是多对多的依赖关系方面。

    相关阅读:Android面试一天一题(Day 43:设计模式)

    2.ListView的notifyDataSetChanged

    当 ListView 的数据发生变化时,调用 Adapter 的 notifyDataSetChanged 函数,这个函数又会调用 DataSetObservable 的 notifyChanged 函数,这个函数会调用所有观察者 (AdapterDataSetObserver) 的 onChanged 方法,在 onChanged 函数中又会调用 ListView 重新布局的函数 requestLayout()使得 ListView 刷新界面。

    七、观察者模式的缺点

    • 开发效率问题。一个被观察者,多个观察者,开发和调试就会比较复杂。
    • 运行效率问题。多个观察者、多级触发等情况下,因为在Java默认是顺序执行,所以一个观察者卡壳,会影响整体的执行效率,这时候推荐考虑采用异步的方式。

    八、观察者模式的优点

    • 观察者和被观察者之间是抽象耦合,观察者和被观察者的扩展比较方便。
    • 建立一套触发机制。例如建立一条触发链。

    九、总结

    观察者模式主要的作用就是对象解耦,将观察者与被观察者完全隔离,只依赖与 Observer 和 Obserable 抽象。例如LisetView就是运用了Adapter和观察者模式使得它的可扩展性、灵活性非常强,而耦合的很低,这些设计模式在Android源码中优秀运用的典范。

    • 那么如何达到低耦合、高灵活性的代码?
    • 广播接收器和时间总线在观察者模式上有什么相似和不相同的地方?
    • RxJava是怎么运用观察者模式的?

    十、参考书籍

    相关文章

      网友评论

          本文标题:设计模式之观察者模式

          本文链接:https://www.haomeiwen.com/subject/zuklfqtx.html