Android设计模式之——观察者模式

一、介绍

观察者模式是一个使用率非常高的模式,它最常用的地方是GUI系统、订阅——发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以GUI系统来说,应用的UI具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也会经常性变化,但是业务逻辑基本变化不大,此时,GUI系统需要一套机制来应对这种情况,使得UI层与具体的业务逻辑解耦,观察者模式此时就派上用场了。

二、定义

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

三、使用场景

  • 关联行为场景,需要注意的是,关联行为是可拆分的,而不是”组合“关系。

  • 事件多级触发场景。

  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

四、观察者模式的UML类图

UML类图:

这里写图片描述

角色介绍:

  • Subject:抽象主题,也就是被观察者(Observable)的角色,抽象主题角色把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

  • ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫做具体被观察者(ConcreteObservable)角色。

  • Observer:抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己。

  • ConcreteObserver:具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便主题的状态发生改变化时更新自身的状态。

五、简单实现

这里举一个追剧的例子,平常为了不错过最新的电视剧我们会订阅或关注这个电视剧,当电视剧更新后会第一时间推送给我们,下来就简单实现一下。

抽象观察者类:

/***  抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己*/
public interface Observer {/***  有更新*  *  @param message 消息*/public void update(String message);}

抽象被观察者类:

/*** 抽象被观察者类*/
public interface Observable {/*** 推送消息* * @param message 内容*/void push(String message);/*** 订阅* * @param observer 订阅者*/void register(Observer observer);
}

具体的观察者类:

/*** 具体的观察者类,也就是订阅者*/
public class User implements Observer {@Overridepublic void update(String message) {System.out.println(name + "," + message + "更新了!");}// 订阅者的名字private String name;public User(String name) {this.name = name;}
}

具体的被观察者类:

/***  具体的被观察者类,也就是订阅的节目*/
public class Teleplay implements Observable{private List<Observer> list = new ArrayList<Observer>();//储存订阅者@Overridepublic void push(String message) {for(Observer observer:list){observer.update(message);}}@Overridepublic void register(Observer observer) {list.add(observer);}}

实现:

public class Client {public static void main(String[] args) {//被观察者,这里就是用户订阅的电视剧Teleplay teleplay = new Teleplay();//观察者,这里就是订阅用户User user1 = new User("小明");User user2 = new User("小光");User user3 = new User("小兰");//订阅teleplay.register(user1);teleplay.register(user2);teleplay.register(user3);//推送新消息teleplay.push("xxx电视剧");}
}

结果:

小明,xxx电视剧更新了!
小光,xxx电视剧更新了!
小兰,xxx电视剧更新了!

由上面的代码可以看出实现了一对多的消息推送,推送消息都是依赖Observer和Observable这些抽象类,而User和Teleplay完全没有耦合,保证了订阅系统的灵活性和可扩展性。

六、Android源码中的观察者模式

1、BaseAdapter

BaseAdapter我相信大家都不陌生,在ListView的适配器中我们都是继承它。下面来简单分析分析。

BaseAdapter 部分代码:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {//数据集观察者private final DataSetObservable mDataSetObservable = new DataSetObservable();public boolean hasStableIds() {return false;}public void registerDataSetObserver(DataSetObserver observer) {mDataSetObservable.registerObserver(observer);}public void unregisterDataSetObserver(DataSetObserver observer) {mDataSetObservable.unregisterObserver(observer);}/*** 当数据集变化时,通知所有观察者*/public void notifyDataSetChanged() {mDataSetObservable.notifyChanged();}
}

看看mDataSetObservable.notifyChanged()方法:

public class DataSetObservable extends Observable<DataSetObserver> {/*** Invokes {@link DataSetObserver#onChanged} on each observer.* Called when the contents of the data set have changed.  The recipient* will obtain the new contents the next time it queries the data set.*/public void notifyChanged() {synchronized(mObservers) {// since onChanged() is implemented by the app, it could do anything, including// removing itself from {@link mObservers} - and that could cause problems if// an iterator is used on the ArrayList {@link mObservers}.// to avoid such problems, just march thru the list in the reverse order.for (int i = mObservers.size() - 1; i >= 0; i--) {mObservers.get(i).onChanged();}}}
}

可以看出在mDataSetObservable.notifyChanged()中遍历所有观察者,并调用他们的onChanged(),从而告知观察者发生了什么。

那么观察者怎么来的,那就是setAdapter方法,代码如下:

    @Overridepublic void setAdapter(ListAdapter adapter) {if (mAdapter != null && mDataSetObserver != null) {mAdapter.unregisterDataSetObserver(mDataSetObserver);}resetList();mRecycler.clear();if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);} else {mAdapter = adapter;}mOldSelectedPosition = INVALID_POSITION;mOldSelectedRowId = INVALID_ROW_ID;// AbsListView#setAdapter will update choice mode states.super.setAdapter(adapter);if (mAdapter != null) {mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();mOldItemCount = mItemCount;mItemCount = mAdapter.getCount();checkFocus();mDataSetObserver = new AdapterDataSetObserver();mAdapter.registerDataSetObserver(mDataSetObserver);//注册观察者......省略}}

AdapterDataSetObserver定义在ListView的父类AbsListView中,是一个数据集观察者,代码:

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {@Overridepublic void onChanged() {super.onChanged();if (mFastScroller != null) {mFastScroller.onSectionsChanged();}}@Overridepublic void onInvalidated() {super.onInvalidated();if (mFastScroller != null) {mFastScroller.onSectionsChanged();}}
}

它由继承自AbsListView的父类AdapterView的AdapterDataSetObserver, 代码如下 :

class AdapterDataSetObserver extends DataSetObserver {private Parcelable mInstanceState = null;// 上文有说道,调用Adapter的notifyDataSetChanged的时候会调用所有观察者的onChanged方法,核心实现就在这里@Overridepublic void onChanged() {mDataChanged = true;mOldItemCount = mItemCount;// 获取Adapter中数据的数量mItemCount = getAdapter().getCount();// Detect the case where a cursor that was previously invalidated has// been repopulated with new data.if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null&& mOldItemCount == 0 && mItemCount > 0) {AdapterView.this.onRestoreInstanceState(mInstanceState);mInstanceState = null;} else {rememberSyncState();}checkFocus();// 重新布局ListView、GridView等AdapterView组件requestLayout();}// 代码省略public void clearSavedState() {mInstanceState = null;}
}

当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged函数,这个函数又会调用DataSetObservable的notifyChanged函数,这个函数会调用所有观察者 (AdapterDataSetObserver) 的onChanged方法。这就是一个观察者模式!

七、总结

优点:

  • 观察者和被观察者之间是抽象耦合,应对业务变化。

  • 增强系统的灵活性和可扩展性。

缺点:

  • 在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/447010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android设计模式之——备忘录模式

一、介绍 备忘录模式是一种行为模式&#xff0c;该模式用于保存对象当前状态&#xff0c;并且在之后可以再次恢复到此状态&#xff0c;这有点像我们平时说的”后悔药“。备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问&#xff0c;目的是为了保护好被保存…

c++ memory 头文件详细介绍

类 指针特征 pointer_traits (C11) 提供关于指针式类型的信息 (类模板) 垃圾收集器支持 pointer_safety (C11) 列出指针安全模式 (枚举) 分配器 allocator 默认的分配器 (类模板) allocator_traits (C11) 提供关于分配器类型的信息 (类模板) allocator_arg_t (C11) 标签类型…

C++ using的三种使用策略以及具体的用法

Using的使用方法 1&#xff0c;命名空间的使用 为了防止代码冲突&#xff0c;都会使用到命名空间。假设这样一种情况&#xff0c;当一个班上有两个名叫 Zara 的学生时&#xff0c;为了明确区分他们&#xff0c;我们在使用名字之外&#xff0c;不得不使用一些额外的信息&#…

Android设计模式之——迭代器模式

一、介绍 迭代器模式&#xff08;Iterator Pattern&#xff09;又称为游标&#xff08;Cursor&#xff09;模式&#xff0c;是行为型设计模式之一。迭代器模式算是一个比较古老的设计模式&#xff0c;其源于对容器的访问&#xff0c;比如Java中的List、Map、数组等&#xff0c…

Android设计模式之——模板方法模式

一、介绍 在面向对象开发过程中&#xff0c;通常会遇到这样的一个问题&#xff0c;我们知道一个算法所需的关键步骤&#xff0c;并确定了这些步骤的执行顺序&#xff0c;但是&#xff0c;某些步骤的具体实现是未知的&#xff0c;或者说某些步骤的实现是会随着环境的变化而改变…

Android设计模式之——访问者模式

一、介绍 访问者模式是一种将数据操作与数据结构分离的设计模式&#xff0c;它是《设计模式》中23种设计模式中最复杂的一个&#xff0c;但它的使用频率并不高&#xff0c;正如《设计模式》的作者GOF对访问者模式的描述&#xff1a;大多数情况下&#xff0c;你不需要使用访问者…

C++类模板template <class T>简单使用方法

一个简单的例子 两个数比大小 如果两个数都是int类型 class Compare_int { public :Compare(int a,int b){xa;yb;}int max( ){return (x>y)?x:y;}int min( ){return (x<y)?x:y;} private :int x,y; }; 如果两个数是float类型 class Compare_float { public :Compare(…

Android设计模式之——中介者模式

一、介绍 中介者模式&#xff08;Mediator Pattern&#xff09;也称为调解者模式或调停者模式&#xff0c;Mediator本身就有调停者和调解者的意思。 在日常生活中调停者或调解者这个角色我们见得比较多的是“和事老”&#xff0c;也就是说调解两个有争端的人的角色&#xff0…

C++智能指针中unique_ptr部分内容的讲解

参考链接 std::unique_ptr 介绍 定义位于头文件<memory>std::unique_ptr 是通过指针占有并管理另一对象&#xff0c;并在 unique_ptr 离开作用域时释放该对象的智能指针。 在下列两者之一发生时用关联的删除器释放对象&#xff1a;1&#xff0c;销毁了管理的 unique_pt…

Java基础——Java多线程中sleep()、wait()和notify()

一、sleep()sleep()方法源码&#xff1a;/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does …

Key_handle的学习

代码 一切尽在不言中 #pragma once#include "common/common.h" #include "sdf/sdf.h"#include <memory>namespace sdf {namespace algorithm {class KeyHandle {public:using erased_internal_data_t char; //使用erased_internal_data_t等效于ch…

Java基础——虚拟机结构

一、Java平台结构图二、JVM、JRE和JDK关系JVM&#xff1a;Java Virtual Machine&#xff08;Java虚拟机&#xff09;&#xff0c;负责执行符合规范的Class文件 JRE&#xff1a; Java Runtime Environment &#xff08;java运行环境&#xff09;&#xff0c;包含JVM和类库 JDK&a…

解决 SSH Connection closed by foreign host 问题

用 Xshell 连接服务器总是报错 : Connection closed by foreign host.Disconnected from remote host... 原因可能是 SSH 服务器没设置保活时间间隔 , 具体设置如下 : 操作 # vim /etc/ssh/sshd_config 添加两行 , 或去掉注释 : ClientAliveInterval 60ClientAliveCountMax…

Java基础——synchronized

synchronized重要&#xff01;重要&#xff01;重要&#xff01;重要的事情说三遍&#xff0c;一定要记下来哦。 Java语言的关键字&#xff0c;当它用来修饰一个方法或者一个代码块的时候&#xff0c;能够保证在同一时刻最多只有一个线程执行该段代码。一、当两个并发线程访问同…

C++:MAC安装Boost库文件并且使用CLion开发

boost的filestem库 C在17版本的标准库中引入了一个filesystem库&#xff0c;用来处理文件路径&#xff0c;以及文件访问。很多编译器对filesystem库的支持还不是很好。为了解决这个问题&#xff0c;可以临时使用boost::filesystem来替代。其实C17标准中的filesystem库就是从bo…

Java基础——Java异常处理机制

一、引言 try…catch…finally恐怕是大家再熟悉不过的语句了&#xff0c;而且感觉用起来也是很简单&#xff0c;逻辑上似乎也是很容易理解。不过&#xff0c;我亲自体验的“教训”告诉我&#xff0c;这个东西可不是想象中的那么简单、听话。不信&#xff1f;那你看看下面的代码…

clion在使用sqlite3的时候,显示Undefined symbols for architecture x86_64错误的解决办法

显示Undefined symbols for architecture x86_64错误的原因 1、缺少静态库 环境&#xff1a;在模拟器上报错但在真机上能运行成功&#xff0c;而且报的错误来自于第三方库。原因&#xff1a;architecture x86_64 是指模拟器的架构&#xff0c;意思就是 Crypto 变量在模拟器架…

Java基础——Java反射机制及IoC原理

一、概念 主要是指程序可以访问&#xff0c;检测和修改它本身状态或行为的一种能力&#xff0c;并能根据自身行为的状态和结果&#xff0c;调整或修改应用所描述行为的状态和相关的语义。在java中&#xff0c;只要给定类的名字&#xff0c; 那么就可以通过反射机制来获得类的所…

Ubuntu boost库文件安装编译

简单介绍 Boost库是为C语言标准库提供扩展的一些C程序库的总称&#xff0c;由Boost社区组织开发、维护.Boost向来有准标准库之称&#xff0c;很多新特性例如智能指针等都是先在boost中实现&#xff0c;后来被吸收到标准库之中. Boost实现了日志、算法、日期、地理、数学、线程…

Java基础——类加载机制及原理

一、什么是类的加载&#xff1f; 类的加载指的是将类的.class文件中的二进制数据读入到内存中&#xff0c;将其放在运行时数据区的方法区内&#xff0c;然后在堆区创建一个java.lang.Class对象&#xff0c;用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Cl…