原文链接
by Robert C. Martin(Uncle Bob)
前言
这篇文章写于1996年,里边清晰而简明的介绍了“依赖反转原则”,对了解Dependency Injection很有帮助。今天看来,仍然具有很好的释疑作用。
介绍
我上一篇96年的文章谈到了 Liskov Substitution Principle(LSP)。 这个原则应用到了C++时,为使用公开(public)继承(inheritance)提供了指导。 该原则说,每一个操作一个基类的引用或者指针的函数,都应能够同样操作该基类的派生类,即使其对该派生类一无所知。这就意味着,派生类的虚成员函数必须与基类的虚成员函数保持一致,并且不应该做更多的事情。也就是说,基类中的虚成员函数必须要在派生类中,并且保证只做有用的工作。如果违背LSP原则,操作基类的引用或指针的函数将不得不检查具体对象的类型,以确保操作正确。而检查对象类型则违反了上周讲到的Open-Closed Principle(OCP)。
在此次专栏里,我们将讨论OCP与LSP结构化带来的启示。 严格使用这些原则所产生的结构,可以被一般化成一个更为基本的原则,我称之为“Dependency Inversion Principle”(DIP)。
背景
如今的操作系统都支持多进程并发执行, 系统一般都存在多种不同的服务运行在多个进程当中。那么,进程与进程之间如何通信,即跨进程通信(IPC, Inter-Process Communication)是如何进行的呢? Linux已有好几种IPC机制:
- Signals: 最早的IPC方式,一个进程通过发送信号给另一个有相同UID/GID的进程或者在同一进程组的进程
- Pipes(包括 named pipes): Pipes是一个单向的用于连接一个进程的标准输出与另一个进程的标准输入的字节流通道
- Sockets: 双向的通道,两个进程通过打开同一个socket进行通信
- Message queues: 一个进程将消息写入消息队列,另一个进程从改队列中读取消息
- Semaphores: 信号量是一个可以被多个进程读写的共有变量
- Shared Memeory: 一个系统的内存区域,通过将其映射到两个不同进程的虚拟地址空间,因此每个进程都可以访问该地址空间
- D-Bus(Desktop Bus): 用于桌面组件与服务通讯的协议
Android设备的系统升级有两种方式:(1)下载更新包到手机后,手动安装,即所谓“卡刷包”的形式更新;(2) 通过 Over-the-air(OTA)的方式更新系统,简称为FOTA(Firmware Over The Air),FOTA升级有两种方式,一种是Full update,即将整个IMG置于升级包中,然后将升级包直接拷贝到系统;一种是Increamental update,即通过增量式的差分包,只是将系统更新的部分打包,然后以打补丁的形式应用到系统。
在介绍FOTA升级流程之前,先来看看Android系统的分区。一般,Android系统有如下几个分区(不同厂商、设备可能有差异):
- /boot: 用于系统启动的分区,主要包括Kernel和ramdisk。主要用于挂载system和其他分区,并加载system分区的代码。没有该分区,手机是无法正常启动的;
ramdisk.img is a small partition image that is mounted read-only by the kernel at boot time. It only contains /init and a few config files. It is used to start init which will mount the rest of the system images properly and run the init procedure. A Ramdisk is a standard Linux feature.
- /system:该分区包含了出了 kernel/ramdisk之外的系统应用程序和库文件;正常操作情况下,该分区是只读状态;
- /recovery:用于系统升级打补丁,包含了一个完整的Linux操作系统和一个特殊的recovery包。FOTA升级时,下载完更新包后,进入recovery模式,recovery程序会读更新包,然后将各个patch应用到各个分区;
- /misc:升级时,recovery用于存储相关信息的小分区
- /data:主要包含了用户的数据,如联系人、短信、设置以及安装的应用。清除该分区等同于将手机重置,OTA不会影响该分区;
- /cache:用于存放需要经常访问使用的数据和应用组件,访问该分区需要特殊的应用程序权限;OTA升级时下载的升级包就位于该分区;