观察者模式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了观察者模式相关的知识,希望对你有一定的参考价值。

参考技术A 观察者模式的定义:
  在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。

观察者模式定义了四种角色:抽象主题、具体主题、抽象观察者、具体观察者。

抽象主题(Subject):该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法。
具体主题(ConcreteSubject):该角色继承或实现了抽象主题,定义了一个集合存入注册过的具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。

抽象观察者(Observer):该角色是具体观察者的抽象类,定义了一个更新方法。
具体观察者(ConcrereObserver):该角色是具体的观察者对象,在得到具体主题更改通知时更新自身的状态。

Android开发模式之观察者模式

  

目录

一、定义

1.观察者模式

2.UML类图

3.观察者模式中的角色

二、使用场景

三、简单实现

四、观察者模式在java.util包中的应用

五、观察者模式在Button中的应用

六、观察者模式在ListView中的应用

七、观察者模式的优缺点

观察者模式的优点

观察者模式的缺点


一、定义

1.观察者模式

        定义对象一种一对多的依赖关系,使得当一个对象改变状态的时候,所有依赖它的对象都会得到通知并自动更新。观察者模式是一种使用频率非常高的设计模式,最常用的地方就是订阅-发布系统。

2.UML类图

3.观察者模式中的角色

  • Subject(抽象主题): 又叫抽象被观察者,把所有的观察者对象引用保存到一个集合里,每个主题都可以由任何数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject(具体主题):又叫具体被观察者,将有关的状态存入具体观察者对象,在内部状态改变时,给所有的登记过的观察者发出通知。
  • Observer(抽象观察者):为所有的观察者定义一个接口,在收到主题通知时进行更新。
  • ConcrereObserver(具体观察者):实现抽象观察者定义的更新接口,在得知主题更改通知时更新自身的状态。

二、使用场景

  • 当一个对象的数据更新了需要通知其他对象,但是又不希望和被通知的对象形成耦合;
  • 当一个对象数据更新了,这个对象需要让其他对象也更新数据但是不知道有多少个对象需要更新。
  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

三、简单实现

1.被观察者抽象类

public interface Observable<T> 
    //绑定观察者
    void addObserver(Observer observer);
    //移除观察者
    void removeObserver(Observer observer);
    //被观察者发出了改变
    void notifyObservers(String message);

2.被观察者实现类

public class ServerObservable implements Observable<String>

    private List<Observer> observers = new ArrayList<>();
    private String message;
    @Override
    public void addObserver(Observer observer) 
        //添加观察者
        observers.add(observer);
    

    @Override
    public void removeObserver(Observer observer) 
        //删除观察者
        observers.remove(observer);
     

    @Override
    public void notifyObservers(String message) 
        //通知观察者有更新的数据
        for (Observer observer:observers)
            observer.update(message);
        
    
 

 3.观察者抽象类

public interface Observer 
    //被观察者改变接受消息,做响应
    void update(String message);

4.观察者实现类

public class User implements Observer
    String info;
    public User(String s) 
        info = s;
    

    //用于更新数据
    @Override
    public void update(String message) 
        System.out.println(info + message);
    

5.测试代码

    //创建观察者    
    User user1 = new User("第一位观察者");
    User user2 = new User("第二位观察者");
    User user3 = new User("第三位观察者");
    //创建被观察者
    ServerObservable observable = new ServerObservable();
    observable.add(user1);
    observable.add(user2);
    observable.add(user3);
    //通知更新
    observable.notifyObservers("需要更新数据");

运行,从结果看当被观察者发出更新数据的通知后所有注册的观察者都会立刻响应到:

四、观察者模式在java.util包中的应用

观察者模式在JDK中就有典型应用,比如java.util.Observable和java.util.Observer类。在使用时,被观察者需要继承java.util.Observable类,观察者需要实现java.util.Observer接口。下面通过一个简单的Demo看一下如何使用JDK中的观察者模式,UML图如下。

 观察者Student的代码

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

public class Student implements Observer 
    private String name;
    public Student(String name) 
        this.name = name;
    

    @Override
    public void update(Observable o, Object arg) 
        System.out.println(name + (String)arg);
    

被观察者Teacher的代码

import java.util.Observable;

public class Teacher extends Observable 

    public void publishMessage(String message) 
        // mark as value changed
        setChanged();
        // trigger notification
        notifyObservers(message);
    

输出结果如下:

JDK中的Observable基类和Observer接口为我们的简单使用观察者模式提供了方便,但它有一个非常明显的缺点:被观察者Teacher需要继承Observable这个基类,但是如果Teacher已经继承其他的类就不能同时再继承Observable类。解决这个矛盾的思路有两种:一是自定义观察者模式,将add、delete、notify等方法写进Teacher类;二是使用代理模式,在Teacher中维护一个Observable类的对象,并且实现同名的方法。

五、观察者模式在Button中的应用

在Android中我们遇到的最常见的观察者模式就是各种控件的监听。

 //注册观察者
        button.setOnClickListener(new View.OnClickListener() 
            //观察者实现类
            @Override
            public void onClick(View view) 

            
        );

在这段代码中,Button就是主题也就是被观察者,通过new出的View.OnClickListenerd对象就是具体的观察者;onClickListener就是抽象的观察者接口;最后通过setOnClickListener把观察者注册到被观察者中,源码如下:


    //在单击视图时调用的回调的接口定义。
    public interface OnClickListener 
       //在单击视图时调用。
        void onClick(View v);
    
//注册观察者 
public void setOnClickListener(@Nullable OnClickListener l) 
        if (!isClickable()) 
            setClickable(true);
        
        getListenerInfo().mOnClickListener = l;
    

//执行点击事件
public boolean performClick() 
        // We still need to call this method to handle the cases where performClick() was called
        // externally, instead of through performClickInternal()
        notifyAutofillManagerOnClick();

        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) 
            playSoundEffect(SoundEffectConstants.CLICK);
            //调用观察者执行onClick
            li.mOnClickListener.onClick(this);
            result = true;
         else 
            result = false;
        

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
    

六、观察者模式在ListView中的应用

在使用ListView时,如果数据改变了需要调用setDataSetChanged()方法来通知ListView更新UI。换一种说法,ListView的UI是观察者,ListView对应的adapter中的数据就是被观察者。它的实现方式就是注册一个观察者到adapter中,以实现监听adapter的数据变化的目的。

 首先看一下listView.setAdapter(adapter);在这个方法中ListView会创建一个

AdapterDataSetObserver类的观察者并且调用adapter的registerDataSetObserver()。

  @Override
    public void setAdapter(ListAdapter adapter) 
        //首先确保移除了所有的观察者
        if (mAdapter != null && mDataSetObserver != null) 
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        
         .....

        if (mAdapter != null) 
            ....

            //创建AdapterDataSetObserver观察者
            mDataSetObserver = new AdapterDataSetObserver();
            //将观察者注册到adapter
            mAdapter.registerDataSetObserver(mDataSetObserver);

            ....
        
             
    
继续看Adapter的registerDataSetObserver方法,发现最终的结果就是把观察者注册到DataSetObservable这个发布者里。
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter 
    @UnsupportedAppUsage
    //被观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
      
    
    public void registerDataSetObserver(DataSetObserver observer) 
        //注册观察者
        mDataSetObservable.registerObserver(observer);
    

    public void unregisterDataSetObserver(DataSetObserver observer) 
        //移除
        mDataSetObservable.unregisterObserver(observer);
    
...
DataSetObservable继承于android.database.Observable,在DataSetObservable中实现了notifyChanged()方法。
package android.database;

 
public class DataSetObservable extends Observable<DataSetObserver> 
   
    public void notifyChanged() 
        synchronized(mObservers) 
            //通知所有的观察者,调用onChanged
            for (int i = mObservers.size() - 1; i >= 0; i--) 
                mObservers.get(i).onChanged();
            
        
    

    
    public void notifyInvalidated() 
        synchronized (mObservers) 
            for (int i = mObservers.size() - 1; i >= 0; i--) 
                mObservers.get(i).onInvalidated();
            
        
    

现在我们看一下adapter.notifyDataSetChanged(),其实就是调用了被观察者的notifyChanged()通知观察者onChanged刷新UI。

   public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter 
    
    public void notifyDataSetChanged() 
        mDataSetObservable.notifyChanged();
    

七、观察者模式的优缺点

观察者模式的优点

解除观察值与主题之间的耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使各自的变化不会影响另一边的变化。

易于扩展,对于同一主题新增观察者时无需修改原有的代码。

观察者模式的缺点

1、依赖关系并未完全解除,抽象主题依然依赖抽象观察者。

2、使用观察者模式需要考虑开发效率和运行效率的问题,程序中包含一个被观察者、多个观察者,开发、调试等会比较复杂,而且java中消息的通知是顺序执行的,如果一个观察者卡顿,会影响整体的执行效率。

3、可能会引起多余的数据通知。

参考资源

Android 源码中的观察者模式 - 掘金 (juejin.cn)

Android设计模式03-观察者模式 - 简书 (jianshu.com)

 

以上是关于观察者模式的主要内容,如果未能解决你的问题,请参考以下文章

观察者模式是啥

观察者模式

设计模式之观察者模式

Android开发模式之观察者模式

[设计模式]观察者模式与订阅模式

设计模式-行为型模式-观察者模式