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

设计模式之观察者模式

作者: 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