本文是总结了各种设计模式,并且分析设计模式在Android源码的应用,一直更新中......
一、面向对象的六大原则
- 单一职责--优化代码的第一步
缩写是SRP,就一个类而言,应该仅有一个引起变化的原因,一个类中应该就是一组相关性很高的函数、数据的封装。 - 开闭原则--让程序更稳定、更灵活
缩写是OCP,软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是关闭的。 - 历史替换--构建扩展性更好的系统
缩写是LSP,所有引用基类的地方必须能透明地使用其子类的对象。 - 依赖倒置--让项目拥有变化的能力 缩写是DIP,指代了一种特定的解耦形式,使得高层次的模块不依赖于低层次的模块的实现细节的目的,依赖模块被颠倒了。
- 接口隔离--系统有更高的灵活性
缩写是ISP,客户端不应该依赖它不需要的接口。另外一种定义是:类间的依赖关系建立在最小的接口上。 - 迪米特--更好的扩展性、 缩写是LOD,一个对象应该对其他对象有最少的了解。
二、单例模式
单例对象的类必须保证只有一个实例存在。
单例是实现方式
饿汉式(静态常量)
/**
* 好处:在类加载的时候便完成了实例化.
* */
public class singleton1 {
private static final singleton1 SINGLETON_1 = new singleton1();
private singleton1() {
}
public static singleton1 getInstance() {
return SINGLETON_1;
}
}
饿汉式(静态代码块)
/**
* 好处:1.在类加载的时候便完成了实例化.
* 2.在初始化的时候可以进行一些属性的初始化.
* */
public class singleton2 {
private static final singleton2 SINGLETON_2;
int anInt;
static {
// 可以从配置文件中去读取一些值
SINGLETON_2 = new singleton2(1);
}
private singleton2(int anInt) {
this.anInt = anInt;
}
public static singleton2 getInstance() {
return SINGLETON_2;
}
}
枚举
public enum Singleton3 {
INSTANCE_3;
public void whatever() {
}
}
懒汉式(线程不安全)
/**
* 好处:1.在需要的时候才回被创建,可以节约内存.
* */
public class singleton4 {
private static singleton3 SINGLETON;
private singleton4() {}
/**
* 线程不安全,不推荐使用
* */
public static singleton4 getInstance() {
if (SINGLETON == null) {
SINGLETON = new singleton3();
}
return SINGLETON;
}
}
懒汉式(线程安全)
/**
* 好处:1.线程安全
* 2.延迟加载
* 3.效率较高
* */
public class singleton5 {
private volatile static singleton4 SINGLETON;
private singleton5() {}
public static singleton5 getInstance() {
if (SINGLETON == null) {
synchronized (singleton5.class) {
if (SINGLETON == null) {
SINGLETON = new singleton4();
}
}
}
return SINGLETON;
}
}
静态内部类
public class Singleton6 {
private Singleton6() {
}
private static class SingletonInstance {
private static final Singleton6 SINGLETON_6 = new Singleton6();
}
public static Singleton6 getInstance() {
return SingletonInstance.SINGLETON_6;
}
}
优点:
- 在内存中只有一个对象,节省内存空间;
- 避免频繁的创建销毁对象,可以提高性能;
- 避免对共享资源的多重占用,简化访问;
- 为整个系统提供一个全局访问点。
缺点:
- 不适用于变化频繁的对象;
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
- 如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;
Android源码中的单例模式
Context获取系统服务,WMS,AMS,这些服务在合适的时候以单例的形式注册在系统中,需要的时候通过Context的getSystemService(String name)获取。Context的实现类ContextImpl,无名英雄LayoutInflater.
三、Builder设计模式
Builder设计模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。为了将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示分离开来。
使用场景:
- 相同的方法,不同的执行顺序,产生不同的事件结果
- 多个部件或者零件,都可以装配到一个对象,产生的结果不同
- 产品类非常复杂,或者产品类的调用顺序不同产生不同的作用
- 初始化一个对象特别复杂,如参数多,且很多参数是默认值
Android源码中的Builder模式实现
- AlertDialog.Buidler
- WindowManager
优点:
- 良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节
- 建造者独立,容易扩展
缺点:
会产生多余的Builder对象以及Directore对象,消耗内存。
四、原型模式
原型模式是一个创建型模式,原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。俗称"克隆"。
使用场景
- 类初始化需要校花非常多的资源,资源包括数据、硬件资源等,通过原型拷贝避免这些消耗
- 通过new产生一个对象需要非常繁琐的数据准备和访问权限,可以使用原型模式
- 一个对象需要提供给其他对象访问,而且各个调用者可能需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
原型模式属于浅拷贝。
Android源码中的原型模式实现
- ArrayList的clone()
- Intent的查找和匹配
优点:
原型模式在内存中二进制流的拷贝,要比直接new一个对象性能好,特别是在一个循环体内产生大量的对象时,原型模式可以很好的体现优点。
缺点:
这既是优点也是缺点,直接在内存拷贝,构造函数不会执行,在实际开发中应该注意这个潜在问题,优点是减少了约束,缺点也是减少了约束,需要在实际应用时考虑。
五、工厂方法模式
工厂方法模式是创建型设计模式之一,定义一个用于创建的接口,让子类决定实例化哪个类。
使用场景
在任何需要生成复杂对象的地方,都可以使用工厂方法模式。复杂对象适合使用工厂模式,用new就可以完成创建的对象无需使用工厂模式。
Android源码中的工厂方法模式实现
- Android中Activity里的各个生命周期方法
- List和Set源码,都继承Conllection接口,而Conllection接口继承于Iterable接口,Iterable接口有iterator方法。
onCreate的启动流程
优点:
工厂方法模式完全符合设计原则,其降低了对象之间的耦合度,而且,工厂方法模式依赖于抽象的架构,其将实例化的任务交由子类去完成,有非常好的扩展性。
缺点:
每次为工厂方法模式添加新的产品时就要编写一个新的产品类,同时还要引入抽象层,这必然会导致类的结构的复杂化,所以,在某些情况比较简单时,是否需要使用工厂模式,就要权衡利弊了。
六、抽象工厂模式
抽象工厂模式是创建型设计模式之一,为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定它们具体类。
使用场景:
一个对象族有相同的约束时可以使用抽象工厂模式。比如:Android、iOS、Window Phone都有短信和拨号软件两者都属于软件的范畴,所在的操作系统不一样,即便是同一家公司出品的软件,代码的实现逻辑也是不一样的,这个时候就可以考虑使用抽象工厂模式来产生Android、iOS、Window Phone下的短信和拨号软件。
Android源码中的抽象工厂方法模式实现
- 抽象工厂方法模式在Android源码中的实现相对较少,从framework的角度来看Activity和Service都可以看作是一个具体的工厂,也可以看做是一个抽象工厂方法的雏形
- 另外一个例子就是Android底层对MediaPlayer的创建
优点:
分离接口和实现,客户端使用抽象工厂方法来创建需要的对象,客户端不知道具体的实现是谁,客户端只是面向产品的接口编程而已,使其中产品实现中解耦,同时基于接口与实现的分离,使抽象工厂方法模式在切换产品类时更加灵活,容易。
缺点:
- 类文件爆炸性增加
- 不太容易扩展新的产品类,因为每当创建一个新的产品类时,就需要修改抽象工厂,那么所有的具体工厂类均会被修改。
七、策略模式
策略模式定义了一系列算法,并将每一个算法封闭起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
使用场景:
- 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
- 需要安全地封装多种同一类型的操作时。
- 出现同一抽象类有多个子类,而又需要使用if-else或者Switch-case来选择具体子类时。
Android源码中的策略模式实现
- 主要是动画的实现
ValueAnimator的流程图
详细设计图(此图来源于网络)
总结:
策略模式主要用来分离算法,在司昂同的行为抽象下有不同的具体实现策略。这个模式很好地演示了开闭原则,也就是定义抽象,注入不同的实现,从而达到很好的可扩展性。
优点:
- 结构清晰明了、使用简单直观
- 耦合度相对而言较低,扩展方便
- 操作封装也更为彻底,数据更为安全
缺点:
- 随着策略的增加,子类也会变得繁多。
八、状态模式
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类
使用场景:
- 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为
- 代码中包含大量与对象状态有关的条件语句。
Android源码中的状态模式实现
- WI-FI管理中的状态模式(WifiSettings)
总结:
状态模式的关键点在于不同的状态下对于同一行为有不同的响应,这其实就是一个将id-else用多态来实现的一个具体案例。
优点:
State模式将所有与一个特定的状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将烦琐的状态判断换成结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性与可维护性。
缺点:
状态模式的使用必然会增加系统类和对象的个数
八、责任链模式
责任链模式是行为型设计模式之一。使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
使用场景:
- 多个对象处理同一请求,但具体由哪个对象处理则在运行时状态决定。
- 在请求处理者不明确的情况下向多个对象中的一个提交一个请求。
- 需要动态指定一组对象处理请求。
Android源码中的责任链模式实现
- 事件的分发处理,每当用户接触屏幕时,Android都会将对应的事件包装成一个事件对象从ViewTree的顶部至上而下地分发传递。
优点:
- 可以对请求者和处理者关系解耦,提高代码的灵活性。
缺点:
- 对链中请求处理者的遍历,如果处理者太多那么遍历必定会影响性能,特别是在一些递归调用中,要慎重。