安卓常见设计模式整理

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_39664285/article/details/84938163
  • 监听器模式
  1. 监听器模式定义:

事件源经过事件的封装传给监听器,当事件源触发事件后,监听器接收到事件对象可以回调事件的方法

  1. 监听器角色:
  • 事件源:具体的事件源,注册特定的监听,才可以对事件进行响应。
  • 事件对象:封装了事件源对象以及与事件相关的信息,是在事件源和事件监听器之间传递信息的角色。
  • 事件监听器:监听事件,并进行事件处理或者转发,必须注册在事件源上。
  1. 使用匿名内部类的方式实现监听事件
  • 首先为要实现监听的对象绑定监听器,例如为一个Button对象绑定一个监听器botton.setOnClickListener();
  • 然后在setOnClickListener()方法中定义一个OnClickListener的匿名内部类,然后重写内部类中的onClick方法。
  • 最后在onClick方法中实现监听事件要实现的逻辑即可。
  1. UML

http://dl2.iteye.com/upload/attachment/0115/5513/8129c489-e633-332b-934b-f1d02da88cd1.png

  1. 执行顺序:
  • 给事件源注册监听器
  • 组件接受外部作用,也就是事件被触发
  • 组件产生一个相应的事件对象,并把此对象传递给与之关联的事件处理器
  • 事件处理器启动,并执行相关的代码来处理该事件。

 

 

  • 工厂模式
  1. 工厂模式定义:

一个用于创建对象的接口,让子类决定实例化哪个类

 

  1. 分类:

普通工厂模式:生产具体的产品,创建的产品是类(Class)
抽象工厂模式:生产抽象的产品,创建的产品是接口(Interface)

 

  1. 特征:

多用于需要生成复杂对象的地方。用new就可以完成创建的对象就无需使用。工厂模式降低了对象之间的耦合度,由于工厂模式依赖抽象的架构,实例化的任务交由子类去完成,所以有很好的扩展性。

 

  1. 工厂模式主要分为四大模块:
  • 抽象工厂,其为工厂方法模式的核心。
  • 具体工厂,其实现了具体的业务逻辑。
  • 抽象产品,是工厂方法模式所创建的产品的父类。
  • 具体产品,为实现抽象产品的某一个具体产品对象。

 

  1. 工厂方法UML

  1. 源码分析
  • public Object getSystemService(String name) {
    if (getBaseContext() == null) {
    throw new IllegalStateException("System services not available to Activities before onCreate()");
    }
    //........
    if (WINDOW_SERVICE.equals(name)) {
    return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
    ensureSearchManager();
    return mSearchManager;
    }
    //.......
    return super.getSystemService(name);
    }
  • getSystemService方法中就是用到了简单工厂模式,根据传入的参数决定创建哪个对象,由于这些对象以单例模式提前创建好了,所以此处不用new了,直接把单例返回就好。

 

  • 单例模式
  1. 单例模式定义:

因程序需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。

  1. 单例模式的特点:
  • 单例模式只能有一个实例。
  • 单例类必须创建自己的唯一实例。
  • 单例类必须向其他对象提供这一实例。
  1. 单例模式区别静态类
  • 单例可以继承和被继承,方法可以被override,而静态方法不可以。
  • 静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。
  • 静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。
  • 基于2 3条,由于单例对象往往存在于DAO层(例如sessionFactory),如果反复的初始化和释放,则会占用很多资源,而使用单例模式将其常驻于内存可以更加节约资源。
  • 静态方法有更高的访问效率。
  • 单例模式很容易被测试。
  1. UML类图

角色介绍:

  • 1Client--高层客户端;
  • 2Singleton--单例类。

实现单例模式主要有如下几个关键点:

  • 1)构造函数不对外开放,一般为Private
  • 2)通过一个静态方法或者枚举返回单例类对象;
  • 3)确保单例类的对象有且只有一个,尤其是在多线程环境下;
  • 4)确保单例类对象在反序列化时不会重新构建对象。
  1. 源码分析:
  • //获取WindowManager服务引用
  • WindowManager   wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
     
  • 其内部就是通过单例的方式持有一个WindowManager并返回这个对象

 

  1. 总结:

通过将单例类的构造函数私有化,使得客户端代码不能通过new的形式手动构造单例类的对象。单例类会暴露一个公有静态方法,客户端需要调用这个静态方法获取到单例类的唯一对象,在获取这个单例对象的过程中需要确保线程安全,即在多线程环境下构造单例类的对象也是有且只有一个,这也是单例模式实现中比较困难的地方。

 

 

  • 观察者模式
  1. 观察者模式定义

一个被观察者管理所有相依于它的观察者物件,并且在本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

  1. 角色
  • 抽象被观察者角色:把所有对观察者对象的引用保存在一个集合中,每个被观察者角色都可以有任意数量的观察者。被观察者提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
  • 抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
  • 具体被观察者角色:在被观察者内部状态改变时,给所有登记过的观察者发出通知。具体被观察者角色通常用一个子类实现。
  • 具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
  1. 适用场景
  • 1) 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  • 2) 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
  • 3) 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
  1. 观察者模式UML

  1. 源码分析

ListView的适配器有个notifyDataSetChange()函数,就是通知ListView的每个Item,数据源发生了变化,各个子Item需要重新刷新一下。

  1. 观察者模式总结

Android中,有很多场景使用了观察者模式,比如Android的源码里:OnClickListenerContentObserverandroid.database.Observable等;还有第三方开源库EventBusRxJavaRxAndroid等。

 

  • 策略模式
  1. 策略模式定义

有一系列的算法,将每个算法封装起来(每个算法可以封装到不同的类中),各个算法之间可以替换,策略模式让算法独立于使用它的客户而独立变化。

  1. 使用场景
  • 针对同一类型的问题有多重解决方式,仅仅是具体行为有差异时。让系统可以动态的选择算法策略。
  • 需要安全的封装多重同一类对象时,调用者不会知道算法策略的具体过程。
  • 一个类有多个子类,并且在调用的时候用ifswitch判断的时候。
  1. 优点
  • 结构清晰明了,消除了代码中的一大串选择语句,而是分离为一个个独立的类,代码更清晰。
  • 耦合度低,都是基于接口调用和实现,便于拓展和修改。
  • 封装更彻底,数据更安全。
  1. 缺点
  • 首先是会产生很多策略类,增加了系统开销。
  • 用户要知道所有的策略类。

 

  1. UML

  1. 源码分析
  • Android在属性动画中使用时间插值器的时候就用到了策略模式。在使用动画时,可以选择线性插值器LinearInterpolator、加速减速插值器AccelerateDecelerateInterpolator、减速插值器DecelerateInterpolator以及自定义的插值器。这些插值器都是实现根据时间流逝的百分比来计算出当前属性值改变的百分比。通过根据需要选择不同的插值器,实现不同的动画效果。
  1. 总结
  • 策略模式主要就是为了分离算法和使用,是系统用于很好的拓展性。

 

  • 适配器模式
  1. 适配器模式定义:

将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作;适配器模式的别名为包装器(Wrapper)模式,它既可以作为类结构型模式,也可以作为对象结构型模式。在适配器模式定义中所提及的接口是指广义的接口,它可以表示一个方法或者方法的集合。

  1. 适配器模式包含三个角色:
  • Target(目标抽象类):目标抽象类定义客户所需的接口,可以是一个抽象类或接口,也可以是具体类。
  • Adapter(适配器类):它可以调用另一个接口,作为一个转换器,对AdapterTarget进行适配。它是适配器模式的核心。
  • Adapter(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类包好了客户希望的业务方法。
  1. 适配器模式的优缺点
  • 优点:
  • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无需修改原有结构。
  • 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一适配者类可以在多个不同的系统中复用。
  • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便的更换适配器,也可以在不修改原有代码的基础上 增加新的适配器,完全复合开闭原则。
  • 缺点:
  • 一次最多只能适配一个适配者类,不能同时适配多个适配者。
  • 适配者类不能为最终类。
  • 目标抽象类只能为接口,不能为类,其使用有一定的局限性。
  1. 适配器模式的适用环境
  • 系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码。
  • 创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。
  • 当想使用一个既有类的接口,但是这个既有类与目前的代码结构不相兼容的时候可以考虑使用适配器模式。
  1. 类适配器模式UML

  1. 对象适配器模式UML

  1. 源码分析
  • 比较典型的有ListViewRecyclerView
  • ListView用于显示列表数据,但列表数据形式多种多样,为了处理和显示不同的数据,我们需要对应的适配器作为桥梁。这样ListView就可以只关心它的每个ItemView,而不用关心这个ItemView具体显示的是什么。而我们的数据源存放的是要显示的内容,它保存了每一个ItemView要显示的内容。ListView和数据源之间没有任何关系,这时候,需要通过适配器,适配器提供getView方法给ListView使用,每次ListView只需提供位置信息给getView函数,然后getView函数根据位置信息向数据源获取对应的数据,根据数据返回不同的View
  • RecyclerView封装了viewholder的回收复用,也就是说RecyclerView标准化了ViewHolder编写Adapter面向的是ViewHolder而不再是View了,复用的逻辑被封装了,写起来更加简单。
  • 提供了一种插拔式的体验,高度的解耦,异常的灵活,针对一个Item的显示RecyclerView专门抽取出了相应的类,来控制Item的显示,使其的扩展性非常强。

 

 

  • 代理模式
  1. 代理模式定义

为其他对象提供一种代理以控制这个对象的访问。代理模式属于结构型模式。代理模式也叫委托模式。在生活中,比如代购、打官司等等,实际上都是一种代理模式。

  1. 角色
  • Subject(抽象主题类):接口或者抽象类,声明真实主题与代理的共同接口方法。
  • RealSubject(真实主题类):也叫做被代理类或被委托类,定义了代理所表示的真实对象,负责具体业务逻辑的执行,客户端可以通过代理类间接的调用真实主题类的方法。
  • Proxy(代理类):也叫委托类,持有对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行。
  • Client(客户端类):使用代理模式的地方。

 

  1. 分类
  • 静态代理
  • 静态代理就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。上面的例子实现就是静态代理。
  • 动态代理
  • 动态代理类的源码是在程序运行期间根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
  1. UML

 

 
 

 

  1. 源码分析
  • AIDL会根据当前的线程判断是否要跨进程访问,如果不需要跨进程就直接返回实例,如果需要跨进程则返回一个代理。
  • 在跨进程通信时,需要把参数写入到Parcelable对象,然后再执行transact函数,AIDL通过生成一个代理类,这个代理类会自动帮我们写好这些操作。而要实现Android的插件化开发,动态代理更是必不可少的。

 

 

  • 迭代器模式
  1. 迭代器模式定义

提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式属于行为型模式。迭代器(Iterator)模式,又叫做游标(Cursor)模式。Java中的MapList等等容器,都使用到了迭代器模式。

  1. 迭代器模式角色:
  • Iterator:迭代器接口,迭代器接口负责定义、访问和遍历元素的接口。
  • ConcreteIterator:具体迭代器类,具体迭代器类的目的主要是实现迭代器接口,并记录遍历的当前位置。
  • Aggregate:容器接口,容器接口负责提供创建具体迭代器角色的接口。
  • ConcreteAggregate:具体容器类,具体迭代器角色与该容器相关联。
  • Client:客户类
  1. 应用场景
  • 遍历一个容器对象时。
  • 实际我们开发中很少使用到迭代器模式。虽然不怎么用得到,但是了解其原理能够让我们在看到相关的源码(如Java中的MapList等等容器)时能够更容易了解源码的相关思想。
  1. 优点
  • 迭代子模式简化了聚集的接口,迭代子具备了一个遍历接口,这样聚集的接口就不必具备遍历接口;
  • 每一个聚集对象都可以有一个或多个迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。因此,一个聚集对象可以同时有几个迭代在进行之中;
  • 由于遍历算法被封装在迭代子角色里面,因此迭代的算法可以独立于聚集角色变化。
  1. 缺点
  • 会产生多余的对象,消耗内存。
  • 会增多类文件。
  • 遍历过程是一个单向且不可逆的遍历。
  • 遍历过程需要注意容器是否改变,若期间改变了,可能会抛出异常。
  1. UML

  1. 源码分析
  • Android源码中,最典型的就是Cursor用到了迭代器模式,当我们使用SQLiteDatabasequery方法时,返回的就是Cursor对象,之后再通过Cursor去遍历数据
  1. 总结
  • 迭代器模式提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示。
  • 将遍历聚合对象中数据的行为提取出来,封装到一个迭代器中,通过专门的迭代器来遍历聚合对象的内部数据,这就是迭代器模式的本质。迭代器模式是“单一职责原则”的完美体现。
  • 当使用迭代器的时候,我们依赖聚合提供遍历。
  • 迭代器提供了一个通用的接口,让我们遍历聚合的项,放我们编码使用聚合项时,就可以使用多态机制。

参考文献:

  1. https://www.jianshu.com/p/b2d62447c9ea
  2. https://www.cnblogs.com/jackson-zhangjiang/p/7784694.html
  3. https://www.cnblogs.com/jackson-zhangjiang/p/7784694.html
  4. https://www.cnblogs.com/jackson-zhangjiang/p/7784694.html
  5. http://book.51cto.com/art/201511/498711.htm
  6. https://www.cnblogs.com/cielosun/p/6582333.html
  7. https://blog.csdn.net/jason0539/article/details/45055233
  8. https://www.jianshu.com/p/bb9b46ac41e4
  9. https://www.cnblogs.com/songyaqi/p/4805820.html
  10. https://www.jianshu.com/p/a0e687e0904f
  11. https://blog.csdn.net/self_study/article/details/52502709
  12. https://www.jianshu.com/p/f4917cb02752

猜你喜欢

转载自blog.csdn.net/weixin_39664285/article/details/84938163