断断续续的,《Android源代码设计模式解析》也看了一遍。书中提到了非常多的设计模式。可是有部分在开发中见到的几率非常小,所以掌握不了也没有太大影响。

我认为这本书的最大价值有两点,一个是从设计模式的角度去理解Android源代码,结合着日常开发中的经常使用类,对设计模式的理解会更加的深刻;另外一个优点就是了解经常使用模式。再看其它人写的代码的时候,更easy理解代码思路。

以下是我的读书笔记和一些思考,设计模式仅仅整理我认为重要的部分。

建造者模式

建造者模式最明显的标志就是Build类,而在Android中最经常使用的就是Dialog的构建。Notification的构建也是标准的建造者模式。

建造者模式非常好理解,假设一个类的构造须要非常多參数。并且这些參数并不都是必须的,那么这样的情况下就比較适合Builder。

比方构建一个AlertDialog。标题、内容、取消button、确定button、中立button。你可能仅仅须要单独设置几个属性就可以;另外在我的OkHttpPlus项目中,构造一个Http请求也是这样的。有可能你仅仅须要设置URL,有可能须要加入请求參数、Http Header等,这个时候建造者模式也是比較合适的。

单例模式

单例在Android开发中经经常使用到,可是表现形式可能不太一样。

以ActivityManager等系统服务来说,是通过静态代码块的形式实现单例,在首次载入类文件时。生成单例对象,然后保存在Cache中,之后的使用都是直接从Cache中获取。

class ContextImpl extends Context {

    static {
registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
}
}

当然。还有更加明显的样例,比方AccessibilityManager内部自己也保证了单例,使用getInstance获取单例对象。

 public static AccessibilityManager getInstance(Context context) {
synchronized (sInstanceSync) {
if (sInstance == null) { ...... IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
IAccessibilityManager service = iBinder == null
? null : IAccessibilityManager.Stub.asInterface(iBinder);
sInstance = new AccessibilityManager(context, service, userId);
}
}
return sInstance;
}

除此之外。另一些伪单例。比方Application。默认情况下在一个进程中仅仅存在一个实例。可是Application不能算是单例,由于它的构造方法未私有,你能够生成多个Application实例,可是没实用,你没有通过attach()绑定相关信息,没有上下文环境。

public Application() {
super(null);
}

单例的使用场景也非常easy。就是一个App仅仅须要存在一个类实例的情况。或者是类的初始化操作比較耗费资源的情况。在非常多开源框架中,我们仅仅须要一个对象就可以完毕工作,比方各种网络框架和图片载入库。

除此之外,由于单例的实现方式非常多,比方懒汉式、饿汉式、静态内部类、双重锁检查、枚举等方式。所以要清楚每种实现方式的主要特点和使用场景。

原型模式

原型模式在开发中使用的并不多,可是在源代码中却有所体现。

书中以Intent介绍了原型模式,是通过实现Cloneable接口来做的

public class Intent implements Parcelable, Cloneable {
@Override
public Object clone() {
return new Intent(this);
}
}

事实上这样来看的话,原型模式也比較好理解。就是你想更快的获取到一个同样属性的对象。那么就能够使用原型模式,比方这里就获取到了一个Intent对象,Intent里面的属性与被clone的同样,可是两者并无关联。能够单独使用。

除了实现Cloneable接口,你全然能够自定义一个方法,来获取一个对象。我这里以PhoneLayoutInflater为样例介绍。

PhoneLayoutInflater是LayoutInflater的子类,假设我们在Activity中获取LayoutInflate的话。是通过以下方法

 @Override public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}

能够看到,假设为null,就会调用cloneInContext()。这种方法在LayoutInflate是抽象方法,详细实如今PhoneLayoutInflater中

  public LayoutInflater cloneInContext(Context newContext) {
return new PhoneLayoutInflater(this, newContext);
}

能够看到,这也是一个原型模式。所以我们不要太纠结于形式,更重要的是理解这样做的优点。

除了在源代码中能够看到原型模式。在开源框架中也能够看到,比方OkHttpClient中就存在着以下的方法

/** Returns a shallow copy of this OkHttpClient. */
@Override public OkHttpClient clone() {
return new OkHttpClient(this);
}

能够看到,实现和前面的全然同样。也是new了一个对象返回,由于OkHttpClient的构造过程比較复杂,參数众多,所以用这样的方式来直接生成新对象。成本非常低,并且能保留之前对象的參数设置。

工厂方法模式

书中对于工厂方法模式的一个观点非常新鲜,就是Activity.onCreate()能够看做是工厂方法模式,来生成不同的View对象填充界面。

可是我对这个说法不太苟同,原因有两点:一是这样的形式不太符合工厂方法,没有抽象,没有实现,不符合一般格式,也不是静态方法。不可看做是静态工厂方法;二是没有以生成对象为结果,即不是return view来生成对象,仅仅是通过setContentView()来设置了属性而已。

这就像是给一个Activity设置了背景颜色一样。

当然,设计模式这东西一个人有一个人的看法。

静态工厂方法在Android中比較明显的样例应该就是BitmapFactory了,通过各种decodeXXX()就能够从不同渠道获得Bitmap对象,这里不再赘述。

策略模式

在书中策略模式讲得非常好,结合动画的插值器使用方法,我们能够非常好的理解策略模式的形式和使用方法。

在我看来。策略模式就相当于一个影碟机。你往里面插什么碟子,就能放出什么电影。

同样。在OkHttpPlus的封装中,为了对网络返回值进行解析,我使用了策略模式。当然我写代码的时候还不知道策略模式。是写完了之后突然想到。这就是策略模式啊!

策略模式的精髓就在于,你传入一个类,后面的处理就能依照这个类的实现去做。以动画为例。设置不同的插值器对象,就能够得到不同的变化曲线;以返回值解析为例,传入什么样的解析器,就能够把二进制数据转换成什么格式的数据。比方String、Json、XML。

责任链模式

书中对于责任链模式选取的样例非常有代表性。那就是Android的触摸机制,这个看法让我从另一个维度去理解Android中的触摸事件传递。

我在这里提到这个模式,并不想说太多。仅仅是简单的推荐你读一下这一章的内容。相信你也会有收获的。

观察者模式

Android中的观察者模式应该是用的非常频繁的一种模式了。对于这个模式的使用场景就一句话:你想在某个对象发生变化时,立马收到通知。

书中介绍观察者模式使用的是ListView的Adapter为样例。我之前知道Adapter属于适配器模式。不知道这里还有观察者模式的身影。学到了。

Android里面的各种监听器。也都属于观察者模式。比方触摸、点击、按键等。ContentProvider和广播接收者也有观察者模式的身影,能够说是无处不在。

除此之外。如今非常多基于观察者模式的第三方框架也是非常多。比方EventBus、RxJava等等。都是对观察者模式的深入使用。感兴趣的同学能够研究一下。

模板方法模式

这个模式我之前见的比較少,可是理解之后,就会发现这个模式非常easy。

我认为,模板方法模式的使用场景也是一句话:流程确定,详细实现细节由子类完毕。

这里要关注一下『流程』这个关键字。随便拿一个抽象类,都符合”详细实现细节由子类完毕”的要求。关键就在于是否有流程。有了流程,就叫模板方法模式,没有流程。就是抽象类的实现。

书中讲这个模式用的是AsyncTask,各个方法之间的运行符合流程。详细实现由我们完毕,非常经典。

另外一个方面,Activity的生命周期方法能够看做是模板方法模式,各个生命周期方法都是有顺序的。详细实现我们能够重写,是不是和前面的要求非常符合?关于这方面的理解,能够參考我的这篇文章:对Activity生命周期的理解

除了Android里面的模板方法模式。在其它开源项目中也存在着这个模式的运用。比方鸿洋的OkHttp-Utils项目,就是模板方法模式的典型实现。将一个Http请求的过程切割成几部分,比方获取URL,获取请求头。拼接请求信息等步骤,这几个步骤之前有先后顺序,就能够这样来做。

代理模式和装饰器模式

之所以把这两个放在一起说,是由于这两种模式非常像,所以这里简介下他们之间的差别,主要有两点。

  1. 装饰器模式关注于在一个对象上动态的加入方法,而代理模式关注于控制对对象的訪问
  2. 代理模式。代理类能够对它的客户隐藏一个对象的详细信息。因此,当使用代理模式的时候。我们经常在一个代理类中创建一个对象的实例。而当我们使用装饰器模式的时候。通常的做法是将原始对象作为一个參数传给装饰者的构造器

这两句话可能不太好理解,没关系,以下看个样例。

代理模式会持有被代理对象的实例,而这个实例通常是作为成员变量直接存在于代理类中的,即不须要额外的赋值。

比方说WindowManagerImpl就是一个代理类,尽管名字上看着不像,可是它代理的是WindowManagerGlobal对象。从以下的代码中就能够看出来。

public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Display mDisplay;
private final Window mParentWindow; ...... @Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
} @Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
mGlobal.updateViewLayout(view, params);
} @Override
public void removeView(View view) {
mGlobal.removeView(view, false);
} @Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
} @Override
public Display getDefaultDisplay() {
return mDisplay;
}
}

从上面的代码中能够看出,大部分WindowManagerImpl的方法都是通过WindowManagerGlobal实现的,而WindowManagerGlobal对象不须要额外的赋值,就存在于WindowManagerImpl中。另外。WindowManagerGlobal中事实上有大量的方法,可是通过WindowManagerImpl代理之后。都没有暴露出来。对开发人员是透明的。

我们再来看一下装饰器模式。装饰器模式的目的不在于控制訪问,而是扩展功能,相比于继承基类来扩展功能,使用装饰器模式更加的灵活。

书中是以Context和它的包装类ContextWrapper解说的,也非常的典型。我这里就不在赘述了,贴出一些代码来说明装饰器模式的形式。

public class ContextWrapper extends Context {
Context mBase; public ContextWrapper(Context base) {
mBase = base;
}
}

可是另一个问题,就是在ContextWrapper中。全部方法的实现都是通过mBase来实现的,形式上是对上号了,说好的扩展功能呢?

功能扩展事实上是在ContextWrapper的子类ContextThemeWrapper里面。

在ContextWrapper里面。获取系统服务是直接通过mBase完毕的

@Override
public Object getSystemService(String name) {
return mBase.getSystemService(name);
}

可是在ContextThemeWrapper里面,对这种方法进行了重写。完毕了功能扩展

@Override public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}

当然,假设不存在功能扩展就不算是装饰器模式了吗?事实上设计模式本来就是『仁者见仁,智者见智』的事情。仅仅要你能理解这个意思就好。

外观模式

外观模式可能看到的比較少,可是事实上不经意间你就用到了。

这里以我的一个开源项目KLog来说吧,在最開始写这个类的时候,就仅仅有KLog这一个类。完毕主要的Log打印功能,后来又加入了JSON解析、XML解析、Log信息存储等功能,这个时候一个类就不太合适了。于是我把JSON、XML、FILE操作相关的代码抽取到单独的类中。比方JSON打印的代码

public class JsonLog {

    public static void printJson(String tag, String msg, String headString) {

        String message;

        try {
if (msg.startsWith("{")) {
JSONObject jsonObject = new JSONObject(msg);
message = jsonObject.toString(KLog.JSON_INDENT);
} else if (msg.startsWith("[")) {
JSONArray jsonArray = new JSONArray(msg);
message = jsonArray.toString(KLog.JSON_INDENT);
} else {
message = msg;
}
} catch (JSONException e) {
message = msg;
} Util.printLine(tag, true);
message = headString + KLog.LINE_SEPARATOR + message;
String[] lines = message.split(KLog.LINE_SEPARATOR);
for (String line : lines) {
Log.d(tag, "║ " + line);
}
Util.printLine(tag, false);
}
}

代码非常easy,就一个方法,可是在使用的时候,不管打印哪种格式,都是这样使用的

//普通打印
KLog.d(LOG_MSG);
//JSON格式打印
KLog.json(JSON);
//XML格式打印
KLog.xml(XML);

能够看到。尽管功能不同,可是都通过KLog这个类进行了封装。用户仅仅知道用KLog这个类能完毕全部需求就可以,全然不须要知道代码实现是几个类完毕的。

实际上,在KLog内部,是多个类共同完毕打印功能的。

 private static void printLog(int type, String tagStr, Object... objects) {

        if (!IS_SHOW_LOG) {
return;
} String[] contents = wrapperContent(tagStr, objects);
String tag = contents[0];
String msg = contents[1];
String headString = contents[2]; switch (type) {
case V:
case D:
case I:
case W:
case E:
case A:
BaseLog.printDefault(type, tag, headString + msg);
break;
case JSON:
JsonLog.printJson(tag, msg, headString);
break;
case XML:
XmlLog.printXml(tag, msg, headString);
break;
}
}

可是通过外观模式。这些细节对用户隐藏了,这样假设以后我想更换JSON的解析方式,用户的代码不须要不论什么修改,这也是这个设计模式的优势所在。

总结

唠唠叨叨的,总算是把这几种设计模式介绍完了,看完这篇文章。你应该就会发现事实上Android中的设计模式确实到处都存在,不是缺少设计模式,而是缺少一双发现的眼睛。

当然。设计模式的提出是为了解决特定的问题。当我们遇到相似问题的时候,能够从设计模式的角度思考和解决这个问题,这应该是我最大的收获吧。

关于我

江湖人称『凯子哥』,Android开发人员,喜欢技术分享,热爱开源。

《Android源代码设计模式解析》读书笔记——Android中你应该知道的设计模式的更多相关文章

  1. 读书笔记--Android Gradle权威指南(下)

    前言 最近看了一本书<Android Gradle 权威指南>,收获挺多,就想着来记录一些读书笔记,方便后续查阅. 本篇内容是基于上一篇:读书笔记--Android Gradle权威指南( ...

  2. Activity源码解析 - 读书笔记

    1. Activity启动 Activity是一个比较好的模板方法模式.在Android系统启动时,第一个启动的进程是zygote进程,然后由zygote启动SystemServer,再后就是启动AW ...

  3. 《Head First 设计模式》读书笔记

    目录 <Head First 设计模式>读书笔记 创建模式 结构模式 行为模式 用思维导图记录的读书笔记. <Head First 设计模式>读书笔记 模式的分类遵循<设 ...

  4. 读书笔记--Android Gradle权威指南(上)

    本篇文章已授权微信公众号 dasu_Android(大苏)独家发布 最近看了一本书<Android Gradle 权威指南>,对于 Gradle 理解又更深了,但不想过段时间就又忘光了,所 ...

  5. 《Android高级进阶》读书笔记

    <Android高级进阶>是据我所知的市面上唯一一本技术工具书,比较的高大全,作者的目的是为了对全领域有个初步的概念 No1: 在Android系统中,拥有事件传递处理能力的类有以下三种 ...

  6. 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高

    第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...

  7. Android群英传》读书笔记 (3) 第六章 Android绘图机制与处理技巧 + 第七章 Android动画机制与使用技巧

    第六章 Android绘图机制与处理技巧 1.屏幕尺寸信息屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DP ...

  8. Android群英传》读书笔记 (1) 第一章 Android体系与系统架构 + 第二章 Android开发工具新接触

    第一章 Android体系与系统架构 1.Dalvik 和 ARTDalvik好比是一辆可折叠的自行车,平时是折叠的,只有骑的时候,才需要组装起来用.ART好比是一辆组装好了的自行车,装好就可以骑了. ...

  9. Android开发艺术探索读书笔记——进程间通信

    1. 多进程使用场景 1) 应用某些模块由于特殊需求须要执行在单独进程中. 如消息推送,使消息推送进程与应用进程能单独存活,消息推送进程不会由于应用程序进程crash而受影响. 2) 为加大一个应用可 ...

随机推荐

  1. C# 代码实现设置用户"NETWORK SERVICE"具有对文件夹的读取权限。

    设置用户"NETWORK SERVICE"具有对文件夹的读取权限. 原帖地址: http://www.cnblogs.com/sjhrun2001/archive/2009/03/ ...

  2. Lotusscript统计在线用户数

    使用notessession的SendConsoleCommand方法向服务器控制台发送“show inetusers”命令,该命令返回一个结果(字符串),字符串类似如下: admin   192.1 ...

  3. 转 linux下面apache2.0.52+php5+gd2+mysql

    gd2才开始支持真彩图片的创建,所以,,升级服务器,因为原来的安装都是默认的系统安装,也更因为是个菜鸟,所以,安装很困难,起初根据网上一些文章在我的red hat A 3 上安装测试,不过,测试了安装 ...

  4. URI -URL-URN区别

    URI -URL-URN区别 URI (uniform resource identifier)   统一资源标识符,用来唯一的标识一个资源URL(uniform resource locator)  ...

  5. 使用badblocks检测坏块

    命令格式 badblocks [-svw][-b <区块大小>][-o <输出文件>][磁盘装置][磁盘区块数 [启始区块]] 典型的命令如下 # 写测试, 数据安全 -c - ...

  6. TabLayout基本属性全解

    代码地址如下:http://www.demodashi.com/demo/14659.html 前言 之前讲过一篇TabLayout实现顶部导航的文章,这篇文章,来详细介绍下TabLayout的一些基 ...

  7. 【RS】Amazon.com recommendations: item-to-item collaborative filtering - 亚马逊推荐:基于物品的协同过滤

    [论文标题]Amazon.com recommendations: item-to-item collaborative filtering (2003,Published by the IEEE C ...

  8. iOS 用自签名证书实现 HTTPS 请求的原理

    在16年的WWDC中,Apple已表示将从2017年1月1日起,所有新提交的App必须强制性应用HTTPS协议来进行网络请求.默认情况下非HTTPS的网络访问是禁止的并且不能再通过简单粗暴的向Info ...

  9. 【DeepLearning】Exercise:Vectorization

    Exercise:Vectorization 习题的链接:Exercise:Vectorization 注意点: MNIST图片的像素点已经经过归一化. 如果再使用Exercise:Sparse Au ...

  10. 多级菜单系统安装维护shell脚本实现企业级案例

    演示效果: 1.一级菜单 2.二级菜单 3.执行操作 脚本参考: #!/bin/bash #author lic(oldboy linux student) #date 1304 DISK_NO=&q ...