Android中常⽤的⼏种设计模式及在源码中的应⽤
⼀、单例模式
单例模式是⼀种对象创建模式,它⽤于产⽣⼀个对象的具体实例,它可以确保系统中⼀个类只产⽣⼀个实例。
好处:
1. 对于频繁使⽤的对象,可以省略从创建对象的花费时间,这对于那些重量级对象⽽⾔,是⾮常可观的⼀笔系统开销。
2. 由于new操作的次数减少,因⽽对系统内存的使⽤频率也会降低,这将减轻GC压⼒,缩短GC停顿时间。
单例包含:饿汉、懒汉、懒汉线程安全、DCL、静态内部类、枚举六种。
① 饿汉
不⾜之处:⽆法对instance实例化做延时加载
优化: 懒汉
② 懒汉
不⾜之处:在多线程并发下这样的实现是⽆法保证实例是唯⼀的
优化:懒汉线程安全
③ 懒汉线程安全
⽅式1: ⽅法中声明synchronized关键字
⽅式2:同步代码实现,例如:
synchronized(lazySafeSingleton.class) {… …}
不⾜之处:性能效率问题(使⽤synchronized导致性能缺陷)
优化: DCL
④ DCL(双重检验锁机制)
不⾜之处:jvm的即使编译器中存在指令重排序的优化
优化: 静态内部类或枚举
可以解决办法: pirvate static volatile DCLSingleton mInstance = null;
public class DCLSingleton {
private static volatile DCLSingleton singleton =null;
private DCLSingleton (){}
public static DCLSingleton getInstance(){
if(singleton ==null){
synchronized(DCLSingleton .class){
if(singleton ==null){
singleton =new DCLSingleton ();
}
}
}
return singleton;
}
singleton = new DCLSingleton ();这⼀步包含:
1)memory = allocate(); // 1、分配对象内存空间
2)instance(memory); // 2、初始化对象
3)instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null
步骤2 和 步骤3之间不存在 数据依赖关系,⽽且⽆论重排前 还是重排后,程序的执⾏结果在单线程中并没有改变,因此这种重排优化是允许的。
即执⾏流程可能为:
memory = allocate(); // 1、分配对象内存空间
instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null,但是对象还没有初始化完成
instance(memory); // 2、初始化对象
也就是当我们执⾏到重排后的步骤2,试图获取instance的时候,会得到null,因为对象的初始化还没有完成,⽽是在重排后的步骤3才完成,因此执⾏单例模式的代码时候,就会重新在创建⼀个instance实例
指令重排只会保证串⾏语义的执⾏⼀致性(单线程),但并不会关系多线程间的语义⼀致性
所以当⼀条线程访问instance不为null时,由于instance实例未必已初始化完成,这就造成了线程安全的问题
所以需要引⼊volatile,来保证出现指令重排的问题,从⽽保证单例模式的线程安全性
⑤ 静态内部类
优点: jvm本⾝机制保证了线程安全,没有性能缺陷
原因: static final
public class staticInnerSingleton {
private staticInnerSingleton() {}
public static staticInnerSingleton getInstance() {
return SingletonHolder.sInstance;
}
private static class SingletonHolder { // 定义为静态私有内部类
private static final staticInnerSingleton sInstatic = new staticInnerSingleton();
}
}
⑥ 枚举
优点: 写法简单,线程安全
public enum singletonEnum {
// 定义⼀个枚举的元素,它就是singleton的⼀个实例
INSTANCE;
// 定义这个枚举的修改操作
public void doSomething() {
//
system.out.println("⼀些操作...");
}
}
public static void main(string[] args) {
singletonEnum s = singletonEnum.INSTANCE;
s.doSomething();
}
静态内部类/枚举:延迟加载/线程安全/性能优势。
⑦ 使⽤容器
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<>();
public static void registerService(String key, Object instance) {
if (!ainsKey(key)) {
objMap.put(key, instance);
}
}
public static Object getService(String key) {
(key);
}
}
容器这种形式在Android原始代码的获取系统服务中使⽤。
单例模式在Android中的实际运⽤:
Application
⼆、builder(建造者模式/⽣成器模式)
概念: 建造者模式是较为复杂的创建型模型,它将客服端与包含多个组成部分(或部件)的复杂对象的创建过程分离。
使⽤场景: 当构造⼀个对象需要很多参数的时候,并且参数的个数或类型不固定的时候。
builder模式的优点:
① 松散耦合: 建造者/⽣成器模式可以⽤同⼀个构建算法构建出表现完全不同的产品,实现构建和产品表现上的分离。
② 可以很容易的改变产品的内部表⽰。
③ 更好的复⽤性,建造者模式很好的实现构建算法和具体产品实现的分离。
builder模式的缺点:
① 会产⽣多余的builder对象,消耗内存。
② 对象的构建过程暴露。
builder模式在Android中的实际运⽤:
①AlertDialog(主要实现是AlertController类)
②Glide/okhttp
三、Adapter(适配器模式)
适配器模式定义: 将⼀个接⼝转换成客户希望的另外⼀个接⼝,适配器模式使接⼝不兼容的那些类可以⼀起⼯作,其别名为包装器(wrapper)。
类适配器定义: 类的适配器模式把适配的类的API转换为⽬标类的API
对象适配器定义: 与类的适配器⼀样,对象的适配器模式把被适配的类的API转换成为⽬标类的API,与类的适配器模式不同的是,对象的适配器模式不是使⽤继承关系连接到Adaptee类,⽽是使⽤委派关系连接到Adaptee类。
类适配器和对象适配器的区别:
类适配器中的Adapter是继承Adapter类并实现Target接⼝。
⽽对象适配器中的Adapter只是实现Target接⼝,在Adapter中引⽤Adaptee中的⽅法,即使⽤委托/委派关系连接到Adaptee类。
四、装饰模式(Decorator Pattern)
装饰模式定义: 动态的给⼀个对象增加⼀些额外的职责就增加对象功能来说,装饰模式⽐⽣成⼦类实现更为灵活,装饰模式是⼀种对象结构型模式。
使⽤场景:
① 在不影响其他对象的情况下,以动态、透明的⽅式给单个对象添加职责。
② 当不能采⽤继承的⽅式对系统进⾏扩展或者采⽤继承不利于系统扩展和维护时可以使⽤装饰模式。
装饰模式的优点:
① 对于扩展⼀个对象的功能,装饰模式⽐继承更加灵活,不会导致类的个数急剧增加(降低耦合度)。
② 可以通过⼀种动态的⽅式来扩展⼀个对象的功能。
③ 可以对⼀个对象进⾏多次装饰,通过使⽤不同的具体装饰类以及这些装饰类的排列组合。
④ 具体装饰类可以独⽴变化。
装饰模式在Android中的实际运⽤:
Context类簇在装饰模式的应⽤
五、外观模式
概念: 外观设计的主要⽬的载应让外部减少与⼦系统内部多个模块的交互,从⽽让外部能够更简单得使⽤⼦系统,它负责把客户端的请求转发给⼦系统内部的各个模块进⾏处理。
使⽤场景:
① 当你要为⼀个复杂⼦系统提供⼀个简单接⼝时。
② 客户程序与抽象类的实现部分之间存在着很⼤的依赖性。
③ 当你需要构建⼀个层次结构的⼦系统时。
外观模式的优点:
① 由于Facade类封装了各个模块交互的过程,如果今后内部模块调⽤关系发⽣了变化,只需要修改Facade实现就可以了。
② Facade实现可以被多个客服端调⽤的。
外观模式在Android中的实际运⽤:
contextImpl——>android中的外观类
六、组合模式
概念: 将对象以树形结构组织起来,以达成“部分–整体”的层级结构,使得客服端对单个对象和组合对象的使⽤具有⼀致性(不要区别整体和个体的使⽤)。
树的结构–>组合设计模式
使⽤场景:
① 需要表⽰⼀个对象整体或部分层次。
② 让客户能够忽略不同对象层次的变化。
组合模式的优点:
① ⾼层模块调⽤简单
② 节点⾃由增加
组合模式在Android中的实际运⽤:
Android中的View的结构是树形结构
七、策略模式
概念: 定义⼀系列的算法,把他们⼀个个封装起来,并且使他们可互相替换,本模式使得算法可独⽴于使⽤它的客户⽽变化。单例模式的几种实现方式
使⽤场景: ⼀个类定义了多种⾏为,并且这些⾏为在这个类的⽅法中以多个条件语句的形式出现,那么可以使⽤策略模式避免在类中使⽤⼤量的条件语句。
策略模式的优点:
① 上下⽂(context)和具体策略(concreteStrategy)是松耦合关系。
② 策略模式满⾜"开–闭"原则。
策略模式在Android中的实际运⽤:
Volley中对HttpStack的设计⽤到的就是策略模式
⼋、模板⽅法模式
概念: 模板⽅法是通过定义⼀个算法⾻架,⽽将算法中的步骤延迟到⼦类,这样⼦类就可以复写这些
步骤的实现来实现特定的算法。
使⽤场景:
① 多个⼦类公有的⽅法,并且逻辑基本相同时。
② 重要、复杂的算法,可以把核⼼算法设计为模板⽅法。
③ 重构时,模板⽅法模式是⼀个经常使⽤的模式。
抽象模板/具体模板:
定义的数量和类型/模板⽅法的数量
模板⽅法模式在Android中的实际运⽤:
① Activity(onCreate()、 onStart()、onResume()…)
② Fragment
③AsyncTask
有关AsyncTask相关知识点:
① Future接⼝:⽤来获取异步计算结果的,说⽩了就是对具体的Runnable或Callable对象任务执⾏的结果进⾏获取(get())、取消(cancel()),判断是否完成任务。
② FutureTask: FutureTask除了实现Future接⼝外还实现了Runnable接⼝,因此FutureTask也可以直接提交给Executor执⾏。九、观察者模式
概念: 定义对象之间的⼀种⼀对多依赖关系,使得每当⼀个对象状态发⽣改变时,其相关依赖关系皆得到通知并被通知⾃动更新。
使⽤场景:
① ⼀个抽象模型有两个⽅⾯,其中⼀个⽅⾯依赖另外⼀个⽅⾯。
② ⼀个对象的改变将导致⼀个或多个其他对象也发⽣改变。
③ 需要在系统中创建⼀个触发链。
观察者模式在Android中的实际运⽤:
①回调模式,⽐如Button的onclick就是⽤的回调。
回调模式:实现了抽象类/接⼝的实例实现了⽗类提供的抽象⽅法后,将该⽅法交还给⽗类来处理。
② ListView中的notifyDataChanged
③ Rxjava
⼗、责任链模式
概念: 是⼀个请求有多个对象来处理,这些对象是⼀条链,但具体由哪个对象来处理,根据条件判断来确定,如果不能处理会传递给该链中的下⼀个对象,直到有对象处理它位置。
使⽤场景:
① 有多个对象可以处理同⼀个请求,具体哪个对象处理该请求待运⾏时刻再确定。
② 在不明确指定接收者的情况下,向多个对象中的⼀个提交⼀个请求。
③ 可多头指定⼀组对象处理请求,客服端可以动态创建职责链来处理请求。
责任链模式在Java/Android中的实际运⽤:
① try- catch语句
② Ordered Broadcast
③ ViewGroup / View 事件传递
ViewGroup/View事件传递
主要概念:MotionEvent
包括四种模式:ACTION_DOWN、ACTION_MOVE、ACTION_UP、ACTIOPN_CANCEL
事件分发机制的三个重要⽅法:
① dispatchTouchEvent
② onInterceptTouchEvent
③ onTouchEvent
ViewGoup事件派发流程:
1. 判断⾃⾝是否需要
2. ⾃⾝不需要或者不确定,则询问ChildView
3. 如果⼦childView不需要则调⽤⾃⾝的onTouchEvent
问题1、ViewGroup中可能有多个childView,如何判断应该分配给哪⼀个?
答:把所有的childView(⼦View)遍历⼀遍,如果⼿指所触摸的点在所在的childView的区域就分发给该childView
问题2、当该点的childView有重叠时应该如何分配?
答:⼀般会分配给最上层的childView。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论