Java设计模式设计模式

《设计模式》观察者模式

2019-08-09  本文已影响3人  敏捷Studio

定义

定义对象间的一对多依赖关系,当一个对象的状态发生改变并发送通知时,所有依赖于它的对象都得到通知并被自动更新。

介绍

UML类图

观察者模式UML类图

角色说明:

实现

继续以送快递为例,快递员有时只是把快递拉到楼下,然后就通知收件人下楼去取快递。

1、 创建抽象观察者。定义一个接到通知的更新方法,即收件人收到通知后的反应:

// 抽象观察者
public interface Observer {
  // 更新方法
  public void update(String message);
}

2、创建具体观察者。实现抽象观察者中的方法,这里创建两个类,一个男孩类和一个女孩类,定义他们收到通知后的反应:

public class Boy implements Observer {
  private String name;

  public Boy(String name) {
    this.name = name;
  }

  @Override
  public void update(String message) {
    // 男孩的具体反应
    System.out.println(name + ",收到了信息:" + message + "屁颠颠的去取快递。");
  }
}

public class Girl implements Observer {
  private String name;

  public Girl(String name) {
    this.name = name;
  }

  @Override
  public void update(String message) {
    // 女孩的具体反应
    System.out.println(name + ",收到了信息:" + message + "让男朋友去取快递!");
  }
}

3、创建抽象主题。即抽象被观察者,定义添加,删除,通知等方法:

// 抽象被观察者
public interface Observable {
  // 添加观察者
  void add(Observer observer);

  // 删除观察者
  void remove(Observer observer);

  // 通知观察者    
  void notify(String message);
}

4、创建具体主题。即具体被观察者,也就是快递员,派送快递时根据快递信息来通知收件人让其来取件:

// 快递员
public class Postman implements Observable {
  // 保存收件人(观察者)的信息
  private List<Observer> personList = new ArrayList<Observer>();
  
  @Override
  public void add(Observer observer) {
    // 添加收件人
    personList.add(observer);
  }

  @Override
  public void remove(Observer observer) {
    // 移除收件人
    personList.remove(observer);
  }

  @Override
  public void notify(String message) {
    // 逐一通知收件人(观察者)
    for (Observer observer : personList) {
      observer.update(message);
    }
  }
}

5、客户端测试

public void test() {
  Observable postman = new Postman();
    
  Observer boy1 = new Boy("路飞");
  Observer boy2 = new Boy("乔巴");
  Observer girl1 = new Girl("娜美");

  postman.add(boy1);
  postman.add(boy2);
  postman.add(girl1);
    
  postman.notify("快递到了,请下楼领取。");
}

输出结果:

路飞,收到了信息:快递到了,请下楼领取。屁颠颠的去取快递。
乔巴,收到了信息:快递到了,请下楼领取。屁颠颠的去取快递。
娜美,收到了信息:快递到了,请下楼领取。让男朋友去取快递!

6、说明:
实际上,JDK内部也内置了Observable(抽象被观察者),Observer(抽象观察者)这两个类,我们也可以直接拿来用,其代码如下:

// 抽象观察者
public interface Observer {
  // 只定义了一个update方法
  void update(Observable o, Object arg);
}

// 抽象被观察者
public class Observable {
  // 定义改变状态,默认为false
  private boolean changed = false;
  // 定义一个观察者list
  private final ArrayList<Observer> observers;

  // 构造函数,初始化一个观察者list来保存观察者
  public Observable() {
    observers = new ArrayList<>();
  }

  // 添加观察者,带同步字段的,所以是线程安全的
  public synchronized void addObserver(Observer o) {
    if (o == null) {
      throw new NullPointerException();
    }
    if (!observers.contains(o)) {
      observers.add(o);
    }
  }

  // 删除观察者
  public synchronized void deleteObserver(Observer o) {
    observers.remove(o);
  }

  // 通知所以观察者,无参数
  public void notifyObservers() {
    notifyObservers(null);
  }

  // 通知所有观察者,带参数
  public void notifyObservers(Object arg) {
    Observer[] arrLocal;
    // 加synchronized字段,保证多线程下操作没有问题
    synchronized (this) {
      // 这里做了是否发生改变的判断,是为了防止出现无意义的更新
      if (!hasChanged()) {
        return;
      }
      // ArrayList转换成一个临时的数组,这样就防止了通知,添加,移除同时发生可能导致的异常
      arrLocal = observers.toArray(new Observer[observers.size()]);
      // 清除改变状态,设置为false
      clearChanged();
    }
  
    // 遍历逐一通知
    for (int i = arrLocal.length - 1; i >= 0; i--) {
      arrLocal[i].update(this, arg);
    }
  }

  // 清除所有观察者
  public synchronized void deleteObservers() {
    observers.clear();
  }

  // 设置被观察者为改变状态,设置为true
  protected synchronized void setChanged() {
    changed = true;
  }

  // 清除改变状态,设置为false
  protected synchronized void clearChanged() {
    changed = false;
  }

  // 返回当前的改变状态
  public synchronized boolean hasChanged() {
    return changed;
  }

  // 观察者数量
  public synchronized int countObservers() {
    return observers.size();
  }
}

应用场景

优缺点

优点

缺点

Android中的源码分析

Android中我们遇到的最常用的观察者模式就是各种控件的监听,如下:

Button button = (Button) findViewById(R.id.button);
// 注册观察者
button.setOnClickListener(new View.OnClickListener() {
  // 观察者实现
  @Override
  public void onClick(View arg0) {
    Log.d("test", "Click button ");
  }
});

上面代码中,Button就是具体的主题,也就是被观察者;new出来的View.OnClickListener对象就是具体的观察者;OnClickListener实际上就是个接口,也就是抽象观察者;通过setOnClickListener把观察者注册到被观察者中。一旦Button捕获到点击事件,即状态发生变化的时候,就会通过回调注册的OnClickListener观察者的onClick方法会来通知观察者,Button状态发生变化。相关源码分析:

// 抽象观察者
public interface OnClickListener {
  // 只有onClick这个方法
  void onClick(View v);
}

// 注册观察者
public void setOnClickListener(@Nullable View.OnClickListener l) {
  if (!isClickable()) {
    // 设置为可点击
    setClickable(true);
  }

  // 把传入的 OnClickListener 对象赋值给了 getListenerInfo().mOnClickListener,即mListenerInfo的mOnClickListener持有OnClickListener对象的引用
  getListenerInfo().mOnClickListener = l;
}

// 返回ListenerInfo对象,这里是一个单例模式
ListenerInfo getListenerInfo() {
  if (mListenerInfo != null) {
    return mListenerInfo;
  }
  mListenerInfo = new ListenerInfo();
  return mListenerInfo;
}

// 执行点击事件
public boolean performClick() {
  final boolean result;
  final ListenerInfo li = mListenerInfo;
  if (li != null && li.mOnClickListener != null) {
    playSoundEffect(SoundEffectConstants.CLICK);
    // 执行onClick方法,li.mOnClickListener即OnClickListener对象
    li.mOnClickListener.onClick(this);
    result = true;
  } else {
    result = false;
  }

  sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
  return result;
}

当我们使用ListView时,需要更新数据时我们就会调用AdapternotifyDataSetChanged()方法,那么我们来看看notifyDataSetChanged()的实现原理,这个方法是定义在BaseAdaper中,具体代码如下:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
  // 数据集被观察者
  private final DataSetObservable mDataSetObservable = new DataSetObservable();

  // 注册观察者
  public void registerDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.registerObserver(observer);
  }

  // 注销观察者
  public void unregisterDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.unregisterObserver(observer);
  }

  // 数据集改变时,通知所有观察者
  public void notifyDataSetChanged() {
    mDataSetObservable.notifyChanged();
  }

   // 其他代码略
}

由上面的代码可以看出BaseAdapter实际上就是使用了观察者模式,BaseAdapter就是具体的被观察者。接下来看看 mDataSetObservable.notifyChanged()的实现:

// 数据集被观察者
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();
      }
    }
  }

  // 其他代码略
}

现在我们看到了有观察者的影子,那么这些观察者是从哪里来的呢?实际上这些观察者是在ListView通过setAdaper()设置Adaper时产生的:

public class ListView extends AbsListView {
  // 其他代码略

  public void setAdapter(ListAdapter adapter) {
    // 如果已存在Adapter,先注销该Adapter的观察者
    if (mAdapter != null && mDataSetObserver != null) {
       mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
    
    // 其他代码略
    
    super.setAdapter(adapter);

    if (mAdapter != null) {
      mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
      mOldItemCount = mItemCount;
      // 获取Adapter中的数据的数量
      mItemCount = mAdapter.getCount();
      checkFocus();

      // 创建一个数据集观察者
      mDataSetObserver = new AdapterDataSetObserver();
      // 注册观察者
      mAdapter.registerDataSetObserver(mDataSetObserver);

      // 其他代码略
    } 
  }
}

从上面的代码可以看到,观察者有了,那么这个观察者主要是干什么的呢?

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
  @Override
  public void onChanged() {
    // 调用父类的onChanged()方法
    super.onChanged();
    if (mFastScroller != null) {
      mFastScroller.onSectionsChanged();
    }
  }

  // 其他代码略
}

AdapterDataSetObserver类中的onChanged()方法没看出啥,继续看他父类的onChanged()方法:

class AdapterDataSetObserver extends DataSetObserver {
  private Parcelable mInstanceState = null;
  // 观察者的核心实现
  @Override
  public void onChanged() {
    mDataChanged = true;
    mOldItemCount = mItemCount;
    // 获取Adapter中的数据的数量
    mItemCount = getAdapter().getCount();
    if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                && mOldItemCount == 0 && mItemCount > 0) {
      AdapterView.this.onRestoreInstanceState(mInstanceState);
      mInstanceState = null;
    } else {
      rememberSyncState();
    }
    checkFocus();
    // 重新布局
    requestLayout();
  }

  // 其他代码略
}

最终就是在AdapterDataSetObserver这个类里面的onChanged()方法中实现了布局的更新。

简单总结:当ListView的数据发生变化时,我们调用AdapternotifyDataSetChanged()方法,这个方法又会调用所有观察者AdapterDataSetObserveronChanged()方法,onChanged()方法又会调用requestLayout()方法来重新进行布局。

BroadcastReceiver作为Android的四大组件之一,实际上也是一个典型的观察者模式。通过sendBroadcast发送广播时,只有注册了相应的IntentFilterBroadcastReceiver对象才会收到这个广播信息,其onReceive方法才会被调起。

另外一些著名的第三方事件总线库,比如RxJavaRxAndroidEventBusotto等等,也是使用了观察者模式,有兴趣的可以去看下他们的源码。

上一篇 下一篇

猜你喜欢

热点阅读