设计模式简介
当我们讨论设计模式的时候,其实我们在讨论面向对象的设计问题。
软件设计中很多问题都会一次又一次的重复出现,而经过一定的总结之后会有一些优秀的解法沉淀下来,可以用于以后出现的类似问题,我们把这些解法叫做设计模式。
软件的生命周期决定了其设计要尽可能地面对(需求)改变,所以在开发和维护过程中,面临的较大问题,除了解决问题的关键点,另一个就是解决需求变更和扩展所带来的代码结构变化问题。而设计模式刚好可以让我们的设计更加灵活,在解决问题的基础上,也使得后期的功能扩展对原有结构的影响尽可能小。
推荐书籍:
- [ 设计模式:可复用面向对象软件的基础 ] [ 推荐书籍 1]
- [Head First 设计模式 ] [ 推荐书籍 2]
[ 推荐书籍 1]: http://item.jd.com/10057319.html ‘ 设计模式:可复用面向对象软件的基础 ‘
[ 推荐书籍 2]: http://item.jd.com/10100236.html ‘Head First 设计模式 ‘
面向对象软件设计原则
[《Head First 设计模式》] [ 推荐书籍 2] 一书里面提炼了很多 OO 设计的原则,在这里分析其中的一些。
- 找出需求中“可能变化”之处,把它们独立出来。 这样,新需求对原架构的影响就会减小很多。这个是程序设计最基本的原则,也可以说成,让每次的变化,尽可能地影响最少的代码。
- 针对接口编程,而不是针对实现编程。 这里的接口,可以是 Interface,也可以是 base class,最关键的是,当行为改变时,对使用该接口的地方来讲是透明的。也就是说,要有效利用多态的特性。
- 多用组合、少用继承。 为什么要少用继承?个人的理解是,继承都需要用在能表现多态的地方。
- 为了交互对象之间的松耦合设计而努力。 这个好像不用解释。
- 要依赖抽象,不要依赖具体类。 一旦依赖了具体类,就没办法低耦合了。
- 一个类只应该有一个引起变化的原因。 这是我们奋斗的目标,但是有时候需要综合其他的原则一起考虑。
还有一些其他原则,有待在实践中不断深化,这里不一一列举了。
设计模式举例
单例模式
单例模式应该是最简单的一个模式,因为它不涉及和其他类交互的问题。但是写好单例模式并不容易,特别是在多线程环境下,单例模式有时候并不好写。
当确定系统只需要某个对象的唯一实例时,就需要使用单例模式。
先看看关系图如下:
最简单的写法就是只提供一个 private 的构造方法,一个 static 的对象和获取这个对象的函数,如下所示:
1 | public class Singleton{ |
不过这种写法即使是在笔试的时候,也的不了多少分的。
实际应用举例
一个比较过关的写法是考虑多线程的情况下能够正常运行,并且尽可能推迟生成实例(在生成实例代价较大的情况下比较有用)。以下是一个具体的场景:要根据手机屏幕的亮灭来控制手机 LED 灯的亮灭,这样可以在无屏幕的状态下根据 LED 的情况判断手机是否处于开机状态。
因为 Framework 中 DisplayPowerState
这个类有在 PowerManagerService
里面更新屏幕的状态,如果不用单例模式,会出现生成多个控制 LED 灯的对象,导致 LED 在指定状态下是一闪一闪的状态,无法达到指定需求。所以考虑使用单例模式来写 LED 对象。
1 | private static class LightHandler extends Handler{ |
考虑目前的 Android 版本使用的 java 都在 1.6 以上,所以 volatile
关键字可以有效使用。实测该方法可以有效控制 LED 灯不再闪烁。
Framework 层代码使用单例模式的例子
github 上有一个很完整的例子:Framework 中的单例模式
这个例子里面解释了 LayoutInflater
使用单例的情况,写得非常详细。里面也提到了 Framework 层的很多 Serivce 都是单例模式,那这里就去看看源代码吧。
.frameworks/base/core/java/com/Android/server/LocalServices.java
这个文件就是典型的例子。
1 | import android.util.ArrayMap; |
每次添加 Service 的时候,都是往 ArrayMap
里面添加一个 key-value
对,通过控制这个数据结构来达到单例的要求。所有的 LoacalServices
在系统中都保持只有一个实例,需要用到的时候,使用 getService()
函数获取即可。可以看到以下一些使用的例子:
1 | ./services/core/java/com/android/server/VibratorService.java:230: mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); |
这些都是需要使用 LoacalServices
的例子。
This is copyright.