观察者模式的Java实现及应用

发表于:2017-11-06 10:59

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Turwe    来源:51Testing软件测试网采编

  观察者模式定义
  观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
  结构
  关键字
  Observable
  即被观察者,也可以被叫做主题(Subject)是被观察的对象。通常有注册方法(register),取消注册方法(remove)和通知方法(notify)。
  Observer
  即观察者,可以接收到主题的更新。当对某个主题感兴趣的时候需要注册自己,在不需要接收更新时进行注销操作。
  例子与应用
  举一个生活中的例子:比如用户从报社订阅报纸,报社和用户之间是一对多依赖,用户可以在报社订阅(register)报纸,报社可以把最新的报纸发给用户(notify),用户自动收到更新。在用户不需要的时候还可以取消注册(remove)。
  再比如Android中的EventBus,Rxjava的实现都是基于观察者模式的思想。再比如回调函数:Android中对Button的点击监听等等。
  观察者模式可以用来解耦
  自己用代码实现一个观察者模式
  现在我们用代码来实现上面订阅报纸的例子:
  NewProvider作为对于报社的抽象,每隔两秒钟向用户发送报纸;User作为用户的抽象,可以收到报纸。NewsModel作为对报纸本身的抽象。
  /**
  * 被观察者接口定义
  */
  public interface MyObserverable {
  void register(MyObserver myObserver);
  void remove(MyObserver myObserver);
  void send(NewsModel model);
  }

  /**
  * 观察者接口定义
  */
  public interface MyObserver {
  void receive(NewsModel model);
  }

  /**
  * 对于报社的抽象,实现了被观察者接口,每隔2s发送一次报纸
  */
  public class NewsProvider implements MyObserverable {
  private static final long DELAY = 2 * 1000;
  private List<MyObserver> mObservers; //我们用一个List来维护所有的观察者对象
  public NewsProvider() {
  mObservers = new ArrayList<>();
  generateNews();
  }
  /**
  * 模拟产生新闻,每个2s发送一次
  */
  private void generateNews() {
  Timer timer = new Timer();
  timer.schedule(new TimerTask() {
  int titleCount = 1;
  int contentCount = 1;
  @Override
  public void run() {
  send(new NewsModel("title:" + titleCount++, "content:" + contentCount++));
  }
  }, DELAY, 1000);
  }
  @Override
  public void register(MyObserver myObserver) {
  if (myObserver == null)
  return;
  synchronized (this) {
  if (!mObservers.contains(myObserver))
  mObservers.add(myObserver);
  }
  }
  @Override
  public synchronized void remove(MyObserver myObserver) {
  mObservers.remove(myObserver);
  }
  @Override
  public void send(NewsModel model) {
  for (MyObserver observer : mObservers) {
  observer.receiveNews(model);
  }
  }
  }

  /**
  * 对于用户的抽象
  */
  public class User implements MyObserver {
  private String mName;
  public User(String name) {
  mName = name;
  }
  @Override
  public void receive(NewsModel model) {
  System.out.println(mName + " receive news:" + model.getTitle() + "  " + model.getContent());
  }
  }

  /**
  * 测试
  */
  public class Test {
  public static void main(String[] args) {
  NewsProvider provider = new NewsProvider();
  User user;
  for (int i = 0; i < 10; i++) {
  user = new User("user:"+i);
  provider.register(user);
  }
  }
  }
  运行结果如下:
  result
  这样我们就自己动手完成了一个简单的观察者模式。
  其实在JDK的util包内Java为我们提供了一套观察者模式的实现,在使用的时候我们只需要继承Observable和Observer类即可,其实观察者模式十分简单,推荐阅读JDK的实现代码真心没有几行。此外在JDK的实现中还增加了一个布尔类型的changed域,通过设置这个变量来确定是否通知观察者。
  下面我们用JDK的类来实现一遍我们的观察者模式:
  public class NewsProvider extends Observable {
  private static final long DELAY = 2 * 1000;
  public NewsProvider() {
  Timer timer = new Timer();
  timer.schedule(new TimerTask() {
  private int titleCount = 1;
  private int contentCount = 1;
  @Override
  public void run() {
  setChanged(); //调用setChagned方法,将changed域设置为true,这样才能通知到观察者们
  notifyObservers(new NewsModel("title:" + titleCount++, "content:" + contentCount++));
  }
  }, DELAY, 1000);
  }
  }

  public class User implements Observer {
  private String mName;
  public User(String name) {
  mName = name;
  }
  @Override
  public void update(Observable observable, Object data) {
  NewsModel model = (NewsModel) data;
  System.out.println(mName + " receive news:" + model.getTitle() + "  " + model.getContent());
  }
  }
  非常简单有木有
  回调函数与观察者模式
  关于回调函数的定义在知乎上看到过一个很赞的解释:
  你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。回答完毕。
  在Android中我们有一个常用的回调:对与View点击事件的监听。现在我们就来分析一下对于View的监听。
  通常在我们使用的时候是这样的:
  xxxView.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
  // do something
  }
  });
  这样我们就注册好了一个回调函数。
  我们可以在View的源码里发现这个接口:
  /**
  * Interface definition for a callback to be invoked when a view is clicked.
  */
  public interface OnClickListener {
  /**
  * Called when a view has been clicked.
  *
  * @param v The view that was clicked.
  */
  void onClick(View v);
  }
  当你setClickListener的时候在View的源码中可以看到对本地OnClickListener的初始化
  /**
  * Register a callback to be invoked when this view is clicked. If this view is not
  * clickable, it becomes clickable.
  *
  * @param l The callback that will run
  *
  * @see #setClickable(boolean)
  */
  public void setOnClickListener(@Nullable OnClickListener l) {
  if (!isClickable()) {
  setClickable(true);
  }
  getListenerInfo().mOnClickListener = l;
  }
  当你的点击到一个View后Android系统经过一系列的调用最后到了View的performClick方法中:
  /**
  * Call this view's OnClickListener, if it is defined.  Performs all normal
  * actions associated with clicking: reporting accessibility event, playing
  * a sound, etc.
  *
  * @return True there was an assigned OnClickListener that was called, false
  *         otherwise is returned.
  */
  public boolean performClick() {
  final boolean result;
  final ListenerInfo li = mListenerInfo;
  if (li != null && li.mOnClickListener != null) {
  playSoundEffect(SoundEffectConstants.CLICK);
  li.mOnClickListener.onClick(this);
  result = true;
  } else {
  result = false;
  }
  sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
  return result;
  }
  就在这里,触发了你的onClick方法,然后执行方法体。
  这里我们的被观察者就是View,他的注册方法(register)就是setOnClickListener(),通知方法就是performClick;而OnClickListener就是观察者。只不过这里的只能注册一个观察对象而已。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号