基础不能少,对设计模式的深入理解对以后的工作也会有很深远的影响!
单例模式
单例模式,可以说是GOF的23种设计模式中最简单的一个。 这个模式相对于其他几个模式比较独立,它只负责控制自己的实例化数量单一(而不是考虑为用户产生什么样的实例),很有意思,是一个感觉上很干净的模式,本人很喜欢这个模式。 android中很多地方都用到了单例模式,本文以输入法管理者InputMethodManager为例,展开分析。 单例模式,Singleton Pattern,能够以其特有的优势, 替代系统中全局变量 ,应用非常广泛。 1.意图 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 2.结构 android中有很多系统级别的全局变量,如时间,输入法,账户,状态栏等等,android中对这些都直接或者有些间接用到了单例模式。 以输入法为例,把上图修改为实际情况: 非常的简单,但是有一点,从上面我们也看到了synchronized关键字,在多线程的环境下,单例模式为了保证自己实例数量的唯一,必然会做并发控制。 类似这种线程安全的单例,跨进程的单例,参数化的单例等等的情况,确实超出本文的范围,而且都涉及到很多东西,是一个很大的话题,不好展开。 3. 代码: public final class InputMethodManager { 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()方法中如下调用: class ContextImpl extends Context{ @Override public Object getSystemService(String name) { if (WINDOW_SERVICE.equals(name)) { //... ... 省略下面n个if,else if } else if (INPUT_METHOD_SERVICE.equals(name)) { //获取输入法管理者唯一实例 return InputMethodManager.getInstance(this); } else if (KEYGUARD_SERVICE.equals(name)) { //... ... 省略下面n个if,else if } else if (ACCESSIBILITY_SERVICE.equals(name)) { //又见单例,无处不在 return AccessibilityManager.getInstance(this); } else if (LOCATION_SERVICE.equals(name)) { //... ... 省略下面n个if,else if } else if (NFC_SERVICE.equals(name)) { return getNfcManager(); } return null; } } 非常简单,干净的一个模式。 4.效果 (1).创建型模式。 (2).对唯一实例的受控访问。 (3).避免全局变量污染命名空间。 (4).允许对操作和表示的精化。 (5).比类操作更灵活。 public class Singleton { /*饿汉式 * private static Singleton instance=new Singleton(); * *private Singleton (){ * *} * *public static Singleton getInstance(){ * return instance; *} */ / *双重校验锁(面试的时候经常写这个) private static Singleton instance; private Singleton(){ } public static Singleton getInstance(){ if(instance==null){ synchronized(Singleton.class){ if(instance==null){ instance=new Singleton(); } } } return instance; } */ /* 静态内部类 private static class Singleton1{ private static Singleton instance=new Singleton(); } private Singleton(){ } public static Singleton getInstance(){ return Singleton1.instance; } */ } / /枚举 public enum Singleton{ instance; public void doSome(){ } public static void main(String []args){ Singleton.instance.doSome(); } } 工厂模式 工厂方法模式,往往是设计模式初学者入门的模式,的确,有人称之为最为典型最具启发效果的模式。 android中用到了太多的工厂类,其中有用工厂方法模式的,当然也有很多工厂并不是使用工厂方法模式的,只是工具管理类。 今天以ThreadFactory举例说明一下简单工厂模式和工厂方法模式。 工厂方法模式,Factory Method,简单的方式,不简单的应用。 1.意图 定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方式模式使一个类的实例化延迟到其子类。 2.结构图和代码 我们先看看标准的工厂方法结构图: 先抽象的产品类,抽象的工厂类,然后用客户端具体的工厂生产相应的具体的产品,但是客户端并不知道具体的产品是怎么生产的,生产的过程封装在工厂里。所以说,某种程度上,工厂方法模式改变了我们直接用new创建对象的方式,一个很好的开始,意义重大。 以ThreadFactory为例: 这张图其实和原本的结构图有细微的区别,那就是参数化得工厂,而且从业务意义上也有些不同,但是思想是一样的。 我们来看下具体的代码: //抽象产品 public interface Runnable { public abstract void run(); } //抽象工厂 public interface ThreadFactory { Thread newThread(Runnable r); } 下面是具体的实现: 比如 AsyncTask类 中工厂的具体实现如下: //工厂实现类 private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } }; //那么产品类在哪里呢? //做为参数Runnable r,我们可以创建千千万万个此系列的产品类 //同理,我们可以创建另外类似的工厂,生产某种专门的线程,非常容易扩展 看到这里,我们一方面为它的生产便利性感叹,一方面又为没创建某类产品都要创建一个工厂而感到繁琐,所以我们下面介绍简单工厂,它的结构图如下: 简单工厂把抽象工厂去掉了,你就创建一个专门生产某类产品就好。在一些特定而又不负责的领域非常实用方便套用这个模式。 在 android中的Connection类 中使用到了这个类: 其中Connection这个抽象类,既充当抽象产品类,也充当具体工厂类。 因为这种情况下,我们往往需要的是马上生产子类,getConnection方法往往是静态的,所以简单工厂,也叫静态工厂方法。 我们看看代码如下: abstract class Connection{ static Connection getConnection( Context context, HttpHost host, HttpHost proxy, RequestFeeder requestFeeder) { if (host.getSchemeName().equals("http")) { return new HttpConnection(context, host, requestFeeder); } // Otherwise, default to https return new HttpsConnection(context, host, proxy, requestFeeder); } } 这就是简单工厂,一个很简单的参数化工厂,真的很简单。 3.效果 1.创建型模式; 2.参数化工厂方法模式得到相应的对象; 3.为子类提供挂钩; 4.连接平行的类层次。 观察者模式 观察者模式,是一种非常常见的设计模式,在很多系统中随处可见,尤其是涉及到数据状态发生变化需要通知的情况下。 本文以AbstractCursor为例子,展开分析。 观察者模式,Observer Pattern,是一个很实用的模式,本人曾经接触到的各种平台以及曾经参与项目中打印模板解释器中都用到了此模式。 1.意图 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 2.结构 这是一个最简单的观察者模式,目标对象能够添加和删除观察者,当自己某种状态或者行为发生改变时,可通过notify通知注册的观察者进行更新操作。 分析AbstractCursor的具体情况,我们发现实际工作有时需要对观察者进行统一管理,甚至观察者类型有很多种而又可以分成几个系列,这个时候是要复杂的多,通过合理的分层这个问题很好解决。下面根据具体情况,我们画出android中abstractCurosr中用到的观察者模式结构图: 观察者分成了两个系列。 3.代码 列举其中相关核心代码如下: public abstract class AbstractCursor { //定义管理器 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类: public abstract class Observable<T> { /** * 观察者列表 */ 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(); } } } 和 public class DataSetObservable extends Observable<DataSetObserver> { /** * 数据发生变化时,通知所有的观察者 */ public void notifyChanged() { synchronized(mObservers) { for (DataSetObserver observer : mObservers) { observer.onChanged(); } } } //... ... (其他方法) } 观察者DataSetObserver类是一个抽象类: public abstract class DataSetObserver { public void onChanged() { // Do nothing } } 所以我们具体看它的子类: public class AlphabetIndexer extends DataSetObserver{ /* * @hide 被android系统隐藏起来了 */ @Override public void onChanged() { //观察到数据变化,观察者做自己该做的事情 super.onChanged(); mAlphaMap.clear(); } } ContentObserver也是类似。 4.效果 (1).行为型模式 (2).目标和观察者间的抽象耦合(经典实现)。 (3). 支持广播通信(相信这点android开发者看到后应该有启发吧) 。 (4).注意意外的更新,这也是观察者更新进行管理的原因之一。 适配器模式 对于android开发者来说起,适配器模式简直太熟悉不过,有很多应用可以说是天天在直接或者间接的用到适配器模式,比如 ListView 。 ListView用于显示列表数据,但是作为列表数据集合有很多形式,有Array,有Cursor,我们需要对应的适配器作为桥梁,处理相应的数据(并能形成ListView所需要的视图)。 正是因为定义了这些适配器接口和适配器类,才能使我们的数据简单灵活而又正确的显示到了adapterview的实现类上。 适配器模式,Adapter Pattern,勇敢的去适配,大量的资源可以重用。 1.意图 适配器模式, 把一个类的接口变换成客户端所期待的另一种接口 ,从而使原本不匹配而无法在一起工作的两个,类能够在一起工作。 适配器模式分为类适配器模式和对象适配器模式。 关于类适配器模式,因为java的单继承,如果继承一个类,另外的则只能是接口,需要手动实现相应的方法。 为了简明直接,我省略了相关的其他适配器 ,只以此两个适配器为例。 ListViews做为client,他所需要的目标接口(target interface)就是ListAdapter,包含getCount(),getItem(),getView()等几个基本的方法,为了兼容List<T>,Cursor等数据类型作为数据源,我们专门定义两个适配器来适配他们:ArrayAdapter和CursorAdapter。这两个适配器,说白了,就是针对目标接口对数据源进行兼容修饰。 这就是适配器模式。 其中BaseAdapter实现了如isEmpty()方法,使子类在继承BaseAdapter后不需要再实现此方法,这就是缺省适配器,这也是缺省适配器的一个最明显的好处。 我们以最简单的若干个方法举例如下,ListAdapter接口如下(,为了简单,我省略了继承自Adapter接口): public interface ListAdapter { public int getCount(); Object getItem(int position); long getItemId(int position); View getView(int position, View convertView, ViewGroup parent); boolean isEmpty(); } 抽象类BaseAdapter,我省略其他代码,只列出两个方法,以作示意: public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { // ... ... public View getDropDownView(int position, View convertView, ViewGroup parent) { return getView(position, convertView, parent); } public boolean isEmpty() { return getCount() == 0; } } ArrayAdapter对List<T>进行封装成ListAdapter的实现,满足ListView的调用: public class ArrayAdapter<T> extends BaseAdapter implements Filterable { private List<T> mObjects; //我只列出这一个构造函数,大家懂这个意思就行 public ArrayAdapter(Context context, int textViewResourceId, T[] objects) { init(context, textViewResourceId, 0, Arrays.asList(objects)); } private void init(Context context, int resource, int textViewResourceId, List<T> objects) { mContext = context; mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mResource = mDropDownResource = resource; mObjects = objects; //引用对象,也是表达了组合优于继承的意思 mFieldId = textViewResourceId; } public int getCount() { return mObjects.size(); } public T getItem(int position) { return mObjects.get(position); } public View getView(int position, View convertView, ViewGroup parent) { return createViewFromResource(position, convertView, parent, mResource); } // ... ... } 我们就如此成功的把List<T>作为数据源以ListView想要的目标接口的样子传给了ListView,同理CursorAdapter也是一模一样的道理,就不写具体代码了。 适配器本身倒是不难,但是提供了解决不兼容问题的惯用模式。 关于什么时候使用适配器模式,大概有三种情况: (1). 你想使用一个已经存在的类,而它的接口不符合你的需求,这个在处理旧系统时比较常见。 (2). 你想创建一个可以复用的类,该类可以和其他不相关的类或不可预见的累协同工作,这就是我们android开发者经常碰到的情况:我们常常自定义一个新的Adapter。 (3). 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配他们的接口,对象适配器可以适配他的父类接口。 3.效果 1.结构性模式 2.上面论述的主要是对象适配器,关于类适配器除了实现目标端口外,还要实现你要兼容的源类,这样可以少写几行代码,但是从组合优于继承的角度看,它总则没有那么的干净。 3.对同一个适配器(即同一个对象)对同样的源进行双向甚至多向的适配,则能使其适用两个甚至多个客户调用。 组合模式 Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是 View和ViewGroup类 的使用。在android UI设计,几乎所有的widget和布局类都依靠这两个类。 组合模式, Composite Pattern,是一个非常巧妙的模式。几乎所有的面向对象系统都应用到了组合模式。 1.意图 将对象View和ViewGroup组合成树形结构以表示"部分-整体"的层次结构(View可以做为ViewGroup的一部分)。 组合模式使得用户对单个对象View和组合对象ViewGroup的使用具有一致性。 2.结构 针对 View和ViewGroup 的实际情况,我们选择安全式的组合模式(在组合对象中添加add,remove,getChild方法),添加少许的注释,我们把上图修改为: 3.代码 View类的实现: public class View{ //... ... //省略了无关的方法 } ViewGroup的实现: 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).使你的设计变得更加一般化。