根据《Android源码设计模式解析与实战》总结归纳学习
面向对象的的六大原则
单一职责
一个类应该是一些相关性很高的函数和数据的集合。例如根据功能进行具体划分,每个个体完成自己相应的事。
开闭原则
开放扩展,关闭修改。在软件生命周期迭代过程中,如果对原有代码进行了修改,很有可能引入新的错误,导致整个项目需要重构,并且需要重新测试啥的。所有要通过扩展的形式来,而不是修改。
里氏代换
子类可以扩展父类功能,但不能改变原有父类的功能
依赖倒置
高层模块不依赖底层模块细节,应该依赖抽象。这个也就是申明使用抽象,实现使用细节。
接口隔离
客户端不应当依赖它不需要的接口,类之间的依赖关系应该建立在最小的接口上,不要将过多的功能加在一个接口上。接口的设计粒度越小,系统越灵活,维护降低
迪米特原则
一个类应该只和自己关心的类进行通信。最少知道原则,降低类与类之间的耦合。
创建型
创建对象实例化,这类模式的特点是不让用户依赖对象的创键。
单例模式
一个类只有一个单例,而且自动实例化并向整个系统提供。
特点:构造器私有,实例静态私有,提供get实例的静态方法
饿汉单例
线程安全,类加载时进行实例化,没有并发问题,但是空间换取时间,不必要提前装载的实例会导致性能问题
1 | public class Singleton { |
懒汉单例
线程不安全,附带延迟加载和缓存思想
1 | public class Singleton { |
简单的懒汉单例可能存在线程安全问题
懒汉单例+同步方法
线程安全,锁住整个方法导致,访问速度下降
1 | public class Singleton { |
双重校验锁单例
只是在实例为空的情况才加锁,内部创键实例加锁,既保证了线程安全,也提升的访问速度
1 | public class Singleton { |
静态单例
线程安全,延迟加载,推荐
1 | public class Singleton { |
枚举单例
线程安全,简单,任何情况下都是一个实例
1 | public enum SingletonEnum { |
容器单例
1 | public class SingletonManager { |
序列化破坏单例
以上单例在除了枚举单例,在反序列化的情况下是会生成新的实例的,如何杜绝,重写以下方法
1 | private Object readResolve() throw ObjectStreamException { |
在android源码中体现
单例模式很常见,比如获得各种系统服务,它们都是以单例的形式注册到Manager的。
Builder模式
对象的构建交给构建器
1 | public class Car{ |
在源码中体现
比如AlertDialog
,OkHttp
中的Request
,RequestBody
,都挺常见的
工厂
用于生产对象
简单工厂模式
也称作静态工厂方法模式
1 | interface Option { |
调用
1 | public class Main { |
可以产出多个不同类型的实例,但是对于后续如果需要加生产出其它的对象,就要对代码进行修改,很明显,这是不符合开闭原则的。对维护和扩展不利
工厂方法模式
1 | interface Option { |
调用
1 | public class Main { |
这样的方式是完全符合开闭原则的,这样的话需要扩展,就直接实现需要的工厂就行了,有良好的灵活性和扩展性,但是随着代码迭代,工厂越来越多,就会在一定程度增加系统复杂度。
抽象工厂模式
以上两种工厂,一个灵活,一个便于扩展,抽象工厂模式就是二者折中的考虑了
1 | interface A {} |
体现
这个也是挺常见的,比如BitmapFactory
原型模式
通过原型实例来创键新的对象
使用场景
- 类初始化需要消耗大量资源,包括数据资源和硬件资源,通过原型拷贝来避免这些消耗
- 通过new来产生实例需要配置繁琐的数据和访问权限
- 一个对象需要提供给其它对象使用,而且各个调用者都有可能改变其值,考虑使用原型拷贝对象提供调用,保护下拷贝
how
1 | class Scratch { |
深拷贝和浅拷贝
为了避免拷贝时产生的副本影响原型,尽量使用深拷贝。
- 浅拷贝:内部对象只是复制引用
- 深拷贝:内部对象需要层层拷贝,而不是引用同一个对象,需要手动clone
行为型
责任链模式
what
每个对象可以处理同一请求,从而避免了请求的发送者和接受者之间的耦合,将这些对象构成一条链,沿着链传递请求,直到有对象处理它为止
体现
android事件传递
这个典型的例子就是android的事件传递
:从Activity的dispatchTouchEvent
传递到window,DoecView,再到ViewGroup的dispatchTouchEvent
,通过onInterceptTouchEvent
判断是否拦截,拦截则自身处理事件,否则传递事件区域的子View,这个View有可能是ViewGroup,如同前面的步骤一样分发,拦截,分发。如果是View通过事件onTouchEvent消费,onTouchEven返回值为true代表消费,表示此次事件处理完毕,返回为false则交由父View消费处理,依次类推。
有序广播
命令模式
定义:将请求封装成对象,让不同的用户使用不同的请求来把客户端参数化,对请求排队或者记录请求日志,以及支持可撤销操作。
解释器模式
定义:给定一个语言,定义它的文法表示,并定义一个解释器,使用解释器来解释语句
体现:PMS对Manifest文件的解析
迭代器模式
定义:提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部表示。
体现:java中Map,List,Set都有,以及android 数据库Cursor游标对象也是迭代器的体现
中介者模式
定义:包含一系列对象的相互作用的方式,中介者使各对象不需要显示的相互引用,从而实现耦合松散,而且可以独立的改变它们之间的交互
体现:
备忘录模式
定义:在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以后可以将该对象恢复到原先保存的状态。
体现:android中Activity的状态保存
观察者模式
定义:定义对象间的一种一对多的依赖关系,使得当一个对象改变状态,所有依赖它的对象会的到某到通知并且做出反应。
体现:RxJava,各种响应事件,事件总线,回调,都是应用观察者模式。
状态模式
定义:当一个对象的内在状态发生改变时允许做出行为的变更。
体现:android系统源码中的状态机,比如蓝牙实体,会有很多状态,在不同的状态间做出不同的响应行为,以及变换状态。
策略模式
定义:策略模式定义了一系列算法,并将每一个算法进行包装起来,而它们还可以相互替换。策略模式让算法独立于使用的它的客户端而独立变化。
体现:android中动画中的插值器,通过定义不同的插值器使得动画的效果发生改变。
模板方法模式
定义:定义一个操作的算法的框架,而将一些步骤延迟到子类,使得子类不改变一个算法的结构即可重定义该算法的某些特定步骤。
体现:AsyncTask的实现,子类只需要实现固定的几个方法即可完成一个异步任务。
访问者模式
定义:封装一些作用于某些数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义总用于这些元素的擦操作。
结构型
适配器模式
定义:把一个类的接口变换成客户端所期待的另一种接口,从而使得原本接口不匹配而无法工作在一起的两个类共同工作。
体现:RecyclerView的Adapter,以及Retrofit的数据转换和Call转换的Adapter
组合模式
定义:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
体现:android View和ViewGroup的view体系
代理模式
定义:为其他对象提供一种代理以控制对这个对象的访问。
体现:ActivityManagerProxy,以及Retrofit的接口动态代理
享元模式
定义:运用共享技术可以有效的支持大量细粒度的对象,对象池,重用对象,减少内存使用
体现:Andorid消息机制中Message,其实就是一个长度50不断复用的消息对象池
外观模式
定义:要求子系统的外部与其内部通信必须通过一个统一的对象进行。提供统一接口进行调用
体现:
- Context将许多功能的具体实现是AMS,PMS等,但是外部大家只需要通过context来调用就行了。
- 代码开发中三级缓存,从内存,数据库,网络请求,但是用户不需要了解细节
桥接模式
定义:将抽象与实现分离,使得它们独立的进行变化
装饰器模式
定义:动态的给对象添加一些额外的职责,就功能来说,装饰模式比生成子类更加灵活
体现:Context 和 ContextWrapper和ContextImpl,我觉得RxJava得中层层Observable和Oberver也是经过包装得。
大话设计模式阅读
- 代码可维护、可扩展、可复用、灵活。
- 面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。