=======================2011-08-26==================================
有时候,最难的是坚持;有时候缺少的是信念。
=======================2011-08-07==================================
从接触设计模式到如今大概4年左右的时间,一直都想有朝一日定要精通,坐于床头,侧望左右,设计模式的书买了7,8本了,也看了很多参考和视频,也用到了一些。但是今天我终于停下来,停下来梳理,停下来欣赏,也停下来反省,总之,我今天停下来了,是为了走的更好,走的更远。
如果有幸我能够把这个系列写到23+N,我想至少我的技术上会为我增色不少,会为我带来更强烈的信心;
如果有幸我能够把这个系列写到23+N,我想至少我去买者方面书籍的时候,我不会那么动心了,我也许不深,你也未必;
如果有幸我能够把这个系列写到23+N,我想至少后面架构和Android的底层我会更加了解,我会看的更清楚,也看的更远。
我自己也是这么想得,所以我会坚持,我要把这个进行到底,写下去,写完23,我写+N,写完SDK,我写别人的开源项目,写完开源,我写我个人项目,写完个人项目,我写我自己的开源项目,至少我要写到我认为我应该继续写的时候,我如是想,也如是做!
我就是这么想的!
=======================2011-07-29==================================
看了很多年的设计模式,也看了很多种设计模式,也在工作中用了几个常见的设计模式。
Android中,包括源码和一些开源项目,用到了很多经典设计模式,而且也用的非常的精彩。
学习Android也有一些日子了,有空的时候看看源代码,看看开源项目的代码,自己也会再工作中写写代码。
今天,斗胆,基于Android(其实就是java),把自己对设计模式的一些粗浅认识,默默的分享出来,希望能得到各位同仁的指点,以期设计能力更进一步。
我将以《设计模式:可复用面向对象软件的基础》为准,发掘Android中各种设计模式的使用情况,提取核心部分,做为实例。
因为不需要写代码,应用场景又一般是Android中自带的,所以文中可能会注重意图介绍和UML结构图的绘制,然后具体设计模式的本身和扩展还需要各位参考其他资料。
下面我列举一些重要的认识点:
设计模式,提供了很多软件工程问题所需处理的解决方案。
根据模式的目的可分为3类:
1.创建型模式:与对象的创建有关。
2.结构性模式:处理类与对象的组合。
3.行为性模式:对类或对象怎样交互和怎样 分配职责进行描述。
面向对象设计的2个基本原则:
1.针对接口编程,而不是针对实现编程。
2.优先使用对象组合,而不是类继承。
面向对象设计的5个设计原则:
1.单一职责原则(SRP)
2.开放封闭原则(OCP)
3.Liskov替换原则(LSP)
4.依赖倒置原则(DIP)
5.接口隔离原则(ISP)
23中设计模式:
1.创建型模式:
(1).工厂方法模式
(2).抽象工厂模式
(3).创建者模式
(4).原型模式
(5).单例模式
2.结构型模式:
(6).适配器模式
(7).桥模式
(8).组合模式
(9).装饰模式
(10).外观模式
(11).享元模式
(12).代理模式
3.行为型模式
(13).解释器模式
(14).模板方法模式
(15).职责链模式
(16).命令模式
(17).迭代器模式
(18).中介者模式
(19).备忘录模式
(20).观察者模式
(21).状态模式
(22).策略模式
(23).访问者模式
除此之外,后来人发现很多新的模式,如空模式等。
下面列举几个常见的问题导致重新设计,可能需要设计模式来分析解决:
1.通过显示的指定一个类来创建对象
2.对特殊操作的依赖
3.对硬件和软件平台的依赖
4.对对象表示或实现的依赖
5.算法依赖
6.紧耦合
7.通过生产子类来扩展功能
8.不能方便的对类进行修改
软件的设计臭味:
1.僵化性
2.脆弱性
3.顽固性
4.粘滞性
5.不必要的复杂性
6.不必要的重复
7.晦涩性
... ...
总而言之,一句话,面向对象特性+原则+模式,折腾来折腾去就是这么个回事。
Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup类的使用。在android UI设计,几乎所有的widget和布局类都依靠这两个类。
组合模式,Composite Pattern,是一个非常巧妙的模式。几乎所有的面向对象系统都应用到了组合模式。
1.意图
将对象View和ViewGroup组合成树形结构以表示"部分-整体"的层次结构(View可以做为ViewGroup的一部分)。
组合模式使得用户对单个对象View和组合对象ViewGroup的使用具有一致性。
热点词汇: 部分-整体 容器-内容 树形结构 一致性 叶子 合成 安全性 透明性
2.结构
针对View和ViewGroup的实际情况,我们选择安全式的组合模式(在组合对象中添加add,remove,getChild方法),添加少许的注释,我们把上图修改为:
3.代码
View类的实现:
?
1
2
3
4 public class View{
//... ...
//省略了无关的方法
}
ViewGroup的实现:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 public abstract class ViewGroup extends View{
/**
* Adds a child view.
*/
public void addView(View child) {
//...
}
public void removeView(View view) {
//...
}
/**
* Returns the view at the specified position in the group.
*/
public View getChildAt(int index) {
try {
return mChildren[index];
} catch (IndexOutOfBoundsException ex) {
return null;
}
}
//other methods
}
4.效果
(1).结构型模式
(2).定义了包含基本对象和组合对象的类层次结构。这种结构能够灵活控制基本对象与组合对象的使用。
(3).简化客户代码。基本对象和组合对象有一致性,用户不用区分它们。
(4).使得更容易添加新类型的组件。
(5).使你的设计变得更加一般化。
观察者模式,是一种非常常见的设计模式,在很多系统中随处可见,尤其是涉及到数据状态发生变化需要通知的情况下。
本文以AbstractCursor为例子,展开分析。
观察者模式,Observer Pattern,是一个很实用的模式,本人曾经接触到的各种平台以及曾经参与项目中打印模板解释器中都用到了此模式。
1.意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
热门词汇:依赖 发布-订阅 事件 通知 更新 监听
2.结构
这是一个最简单的观察者模式,目标对象能够添加和删除观察者,当自己某种状态或者行为发生改变时,可通过notify通知注册的观察者进行更新操作。
分析AbstractCursor的具体情况,我们发现实际工作有时需要对观察者进行统一管理,甚至观察者类型有很多种而又可以分成几个系列,这个时候是要复杂的多,通过合理的分层这个问题很好解决。下面根据具体情况,我们画出Android中abstractCurosr中用到的观察者模式结构图:
观察者分成了两个系列。
3.代码
列举其中相关核心代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
public abstract class
//定义管理器 DataSetObservable mDataSetObservable = new DataSetObservable(); ContentObservable mContentObservable = new ContentObservable(); //注册和卸载两类观察者 public void registerContentObserver(ContentObserver observer) {
mContentObservable.registerObserver(observer); } public void unregisterContentObserver(ContentObserver observer) {
// cursor will unregister all observers when it close if (!mClosed) { mContentObservable.unregisterObserver(observer); } } public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer); } public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer); } //2类通知方法 protected void onChange( boolean selfChange) { synchronized (mSelfObserverLock) { mContentObservable.dispatchChange(selfChange); if (mNotifyUri != null && selfChange) { mContentResolver.notifyChange(mNotifyUri, mSelfObserver); } } } protected void notifyDataSetChange() { mDataSetObservable.notifyChanged(); } } |
再看看Observable<T>类和DataSetObservable类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
public abstract class
/** * 观察者列表 */ protected final ArrayList<T> mObservers = new ArrayList<T>(); public void registerObserver(T observer) { if (observer == null ) { throw new IllegalArgumentException( "The observer is null." ); } synchronized (mObservers) { if (mObservers.contains(observer)) { throw new IllegalStateException( "Observer " + observer + " is already registered." ); } mObservers.add(observer); } } public void unregisterObserver(T observer) { if (observer == null ) { throw new IllegalArgumentException( "The observer is null." ); } synchronized (mObservers) { int index = mObservers.indexOf(observer); if (index == - 1 ) throw new IllegalStateException( "Observer " + observer + " was not registered." ); } mObservers.remove(index); } } public void unregisterAll() { synchronized (mObservers) { mObservers.clear(); } } } |
和
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class DataSetObservable extends Observable<DataSetObserver> { /** * 数据发生变化时,通知所有的观察者 */ public void notifyChanged() { synchronized (mObservers) { for (DataSetObserver observer : mObservers) { observer.onChanged(); } } } //... ... (其他方法) } |
观察者DataSetObserver类是一个抽象类:
1
2
3
4
5
|
public abstract class
public void onChanged() { // Do nothing } } |
所以我们具体看它的子类:
1
2
3
4
5
6
7
8
9
10
11
|
public class AlphabetIndexer extends DataSetObserver{ /* * @hide 被Android系统隐藏起来了 */ @Override public void onChanged() { //观察到数据变化,观察者做自己该做的事情 super .onChanged(); mAlphaMap.clear(); } } |
ContentObserver也是类似。
4.效果
(1).行为型模式
(2).目标和观察者间的抽象耦合(经典实现)。
(3).支持广播通信(相信这点Android开发者看到后应该有启发吧)。
(4).注意意外的更新,这也是观察者更新进行管理的原因之一
单例模式,可以说是GOF的23种设计模式中最简单的一个。
这个模式相对于其他几个模式比较独立,它只负责控制自己的实例化数量单一(而不是考虑为用户产生什么样的实例),很有意思,是一个感觉上很干净的模式,本人很喜欢这个模式。
Android中很多地方都用到了单例模式,本文以输入法管理者InputMethodManager为例,展开分析。
单例模式,Singleton Pattern,能够以其特有的优势,替代系统中全局变量,应用非常广泛。
1.意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
热门词汇:单例 唯一 私有构造
2.结构
Android中有很多系统级别的全局变量,如时间,输入法,账户,状态栏等等,android中对这些都直接或者有些间接用到了单例模式。
以输入法为例,把上图修改为实际情况:
非常的简单,但是有一点,从上面我们也看到了synchronized关键字,在多线程的环境下,单例模式为了保证自己实例数量的唯一,必然会做并发控制。
类似这种线程安全的单例,跨进程的单例,参数化的单例等等的情况,确实超出本文的范围,而且都涉及到很多东西,是一个很大的话题,不好展开。
3. 代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public final class
static final Object mInstanceSync = new Object(); //同步 //内部全局唯一实例 static InputMethodManager mInstance; //对外api static public InputMethodManager getInstance(Context context) {
return getInstance(context.getMainLooper()); } /** * 内部api,供上面的外部api调用 * @hide 系统隐藏的api */ static public InputMethodManager getInstance(Looper mainLooper) {
synchronized (mInstanceSync) { if (mInstance != null ) { return mInstance; } IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE); IInputMethodManager service = IInputMethodManager.Stub.asInterface(b); mInstance = new InputMethodManager(service, mainLooper); } return mInstance; } } |
客户端调用,比如contextimpl中的getSystemService()方法中如下调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class ContextImpl extends Context{ @Override public Object getSystemService(String name) { if (WINDOW_SERVICE.equals(name)) { //... ... 省略下面n个if,else if } else if
//获取输入法管理者唯一实例 return InputMethodManager.getInstance( this ); } else if
//... ... 省略下面n个if,else if } else if
//又见单例,无处不在 return AccessibilityManager.getInstance( this ); } else if
//... ... 省略下面n个if,else if } else if
return getNfcManager(); } return null ; } } |
非常简单,干净的一个模式。
4.效果
(1).创建型模式。
(2).对唯一实例的受控访问。
(3).避免全局变量污染命名空间。
(4).允许对操作和表示的精化。
(5).比类操作更灵活。
模板方法,和单例模式是我认为GOF的23中最简单的两种模式。
但是我个人对模板方法的经典思想特别推崇,虽然模板方法在大对数情况下并不被推荐使用,但是这种通过父类调用子类的方法,使用继承来改变算法的一部分,是面向对象的一种基本认识。
打比方说父亲有很多理想,就行医救人吧,但是父亲医术不行,只能靠儿子,儿子长大后遵从父亲大志,春风拂面,妙手回春,实现了父亲的理想,儿子做的事情早在出生前就定下来了,是父亲之前久定好的模板。
认识到模板方法的这种思想,父类可以让未知的子类去做它本身可能完成的不好或者根本完成不了的事情,对框架学习大有帮助。
本文以View中的draw方法为例,展开分析。
模板方法,TemplateMethod,光是学习这个模式,就会对你产生长远影响的一个模式。
1.意图
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
热门词汇:骨架 步骤 结构 延迟到子类
2.结构
定义了几个步骤1,2,3等,在模板方法中按照一定的结构顺序执行这些步骤。父类的方法可以有缺省实现,也可以是一个空实现,即所谓的钩子操作。
结合实际情况,我们画出View中draw方法涉及到的几个步骤方法如下:
学习模板方法对于我们了解框架的基类实现,生命周期和流程控制非常有帮助,我觉得是务必要掌握的一个模式。
3.代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public class View{ /** * 钩子操作,空实现 */ protected void onDraw(Canvas canvas) { } /** *钩子操作,空实现 */ protected void dispatchDraw(Canvas canvas) { } //算法骨架 public void draw(Canvas canvas) { if (!verticalEdges && !horizontalEdges) { // 步骤1 if (!dirtyOpaque) onDraw(canvas); // 步骤2 dispatchDraw(canvas); // 步骤3 onDrawScrollBars(canvas); return ; } } //... ... } |
我们看看系统组件TextView的实现:
1
2
3
4
5
6
|
public class TextView{ @Override
|