Android学习CursorWrapper与Decorator模式 - Dufresne - 博客园
一 Decorator模式
意图:
动态的给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
动态的给一个对象,而不是对整个类添加额外职责,说明此模式将采用的结构是组合而不是继承;
要给一个对象添加职责,通常可以使用其类的派生类对象替换当前对象,但这显得不够灵活,
继承结构属于静态形式,系统往往并不知道有这样的类存在;
而我们需要的在某些时刻让对象具有一些额外的职责,额外的职责很可能是来自我们自己的扩展或者某些时刻的特定需求等。
于是通过一种方式对现有的对象进行动态的包装(Wrapper)和进行修饰(Decorator),
所以如果能够动态的给对象添加某些职责功能将使系统变得更加灵活和具有扩展性。
适用性:
l 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责;
l 处理那些可以撤销的职责;
l 当不适合采用生成子类的方法进行扩充时:一种情况是支持每一种组合将产生大量子类,
另一种情况是类定义被隐藏无法得知具体的类;
结构:
理解:
在初次学习Decorator模式的时候,我产生如下疑问:
我觉得使用Deccorator或许并不好用,Decorator让对象被装饰进行包装,得到的将是一个被装饰后的对象,而已不是对象本身。
虽然可以通过Decorator传递请求,但势必让事情变得更麻烦。故通常我们仍然需要操作的是原对象本身。
这样一来需要维护两个对象……麻烦,Why?
结果的确是这样:需要通过Decorator传递对原对象的请求,让结构层次变得较深稍微复杂,
如果使用继承(假设让某Commponent继承某Decorator),构造一个更复杂的类,我们就可以直接维护一个对象即可。
但是使用继承我们实际上需要维护的职责功能没有变,仍然是两个部分:Decorator和Component;
在大多数情况下我们可能根本不需要Decorator而只需要Commponent部分,没能做到在需要时动态添加功能;
使用Decorator去动态的包装Component,可以做到“即付即用”的方法添加职责,
也可以逐步的个一个简单对象添加职责构造复杂的对象;对象的维护具有层次性:客户端仅仅需要维护Decorator即可,
被装饰的对象交给Decorator来维护即可,还降低了复杂性。
能做到如此最重要的是:Decorator是一个透明的包装;也就说被包装后的对象和被包装的对象具有一致性,
在使用装饰对象时就像使用原对象一样,而不需要加以区分;故要使Decorator和Component具有一致的接口,
从同一个父类派生下来——保持接口的一致性。
Decorator是对象的装饰包装,Decorator如果太复杂,有可能破坏Decorator与Component的一致性,
故Decorator尽量保持简单所添加的职责单一。
所以Decorator要做到:动态的包装对象,添加对象新的职责,保持与原对象一致性;
二 Android中Cursor应用
Cursor:数据库查询的时候返回的数据集合类型,提供用来访问其中元素的接口;public interface Cursor {};
其中提供了很多方法来访问数据库中存储的元素:光标移动和数据获取等;提供了对于数据查询结果处理通用的接口;
CursorWrapper:public class CursorWrapper implements Cursor 是对Cursor的一个包装,
可以看到实现的接口中都是直接对Cursor接口的包装而没有干其他事情,这样看来好像没有什么意义;
是直接使用Cursor还是使用CursorWrapper没有任何区别;但是CursorWrapper也没有提供任何额外的职责和功能。
CursorWrapper作用:包装一个Cursor类,代理对一个Cursor对象所有的方法调用;主要的用途就是重写Cursor中某些方法进行扩展。
所以仅仅是提供这样一个类,使我们可以重写其中某些方法进行扩展的,直接从Cursor继承需要重写所有接口太多没有意义。
代码中对此使用所见并不多,下面用一个简单的信息排序的例子来学习这种模式。
三 信息对话排序
有这样一个需求:
信息中对话主界面的列表显示是根据时间先后来排序的;对于信息量非常大的用户来说,
有些未读信息可能排在后面查找不是很方便,或者最重要的是:未读信息应排在前,而不应该是按照时间排序。
信息是以数据库的方式进行存储的,进入信息时,查询返回的结果就是一个Curosr类型的对象,然后去更新显示列表;
要对此进行排序首先Cursor本身是不具备此功能的,并且Cursor也是只能读不能写的;
要对信息列表排序,数据在Cursor里面不能改变,怎么排序呢?对显示后的ViewItem进行排序,
但是ViewItem是按需创建的也不行;只能对Cursor进行改造了。
Cursor没有排序的功能那我们就给Cursor增加一个排序共功能的包装;并保证包装后的接口一致;
使其在使用中仍然显示用原来的Cursor一样。
因此按照信息模块的实现框架,调整后其结构如下:
——>ConversationList得到查询后的数据类型Cursor,
——>创建CursorThreadMsgSort对象将其包装起来,
——>再传递给ConversationListAdapter(维护Cursor数据提取)。
——>此时的Cursor就是装饰后的对象,添加了排序的职责。
为什么不使用继承的方式呢?这里可以看到我们从数据库查询得到的是一个Cursor(接口),
而根本不知道具体的类是哪一个;即使知道了也是无法替换的,因为系统提供的是一个通用的数据查询方式,
而排序功能是一个特殊的应用,无法更改;所以只能在使用时动态的进行替换以添加排序的职责。
下面就是排序的具体实现。
四 对Cursor排序实现
对Cursor排序不管怎样实现,Cursor是不能够改变的;能改变的是重写其中的方法,光标移动位置等;
而排序本身就是数据比较位置的移动,Cursor位置不能移动,但是只要得到合适的位置数据,就可以实现排序。
所以我们只要对Cursor建立一张对应的长度和Cursor一致的索引表,表按照规则是经过排序,表中每一项对应的是Cursor中的位置。
在移动光标的时候实际上按照表的对应位置移动即可。
下面看看具体的实现就比较简单了。
可参考:http://blog.csdn.net/yangzongquan/article/details/6547860 对Cursor排序方法。
信息排序实现代码:
package com.android.mms.ui; import android.database.CursorWrapper; import android.database.Cursor; import java.util.ArrayList; import java.util.List; import android.util.Log; import java.util.Collections; import java.util.Comparator; public class ThreadMsgSortCursor extends CursorWrapper { private static final String LOGTAG = "xmp_sort_1"; public static final int SORT_BY_TIME = 1; public static final int SORT_BY_READ = 2; public static final int SORT_BY_NAME = 3; private static final int ID = 0; private static final int DATE = 1; private static final int RECIPIENT_IDS = 3; private static final int READ = 6; private int mSortType ; private int mPos; private List<SortEntry> mSortList; /** * * @param cursor * @param sortType */ public ThreadMsgSortCursor(Cursor cursor,int sortType) { super(cursor); mSortType = sortType; mPos = -1; initSortList(); EexecuteSortList(); } /** * 建立与Cursor对应的索引表 */ protected void initSortList(){ int i = 0; if(mSortList != null){ mSortList.clear(); } else{ mSortList = new ArrayList<SortEntry>(); } while(mCursor.moveToNext()){ SortEntry sortKey = new SortEntry(); sortKey.mNameIds = mCursor.getString(RECIPIENT_IDS); sortKey.mRead = mCursor.getInt(READ); sortKey.mDate = mCursor.getLong(DATE); sortKey.mOrder = i++; Log.v(LOGTAG,sortKey.toString()); mSortList.add(sortKey); } } /** * 对表进行排序 */ protected void EexecuteSortList(){ if(mSortList == null){ return; } ComparatorEx comparator = new ComparatorEx(); Collections.sort(mSortList,comparator); for (int i = 0; i < mSortList.size(); i++) { Log.v(LOGTAG,mSortList.get(i).toString()); } } /** * 索引表数据 */ public class SortEntry{ public String mNameIds; public long mDate; public int mRead; //保存阅读状态 public int mOrder; public String toString(){ String strInfo = " mOrder" + mOrder + "---->" + " mNameIds--> " + mNameIds + " " + "mDate-->" + mDate + " " + "mRead-->" + mRead + " "; return strInfo; } } /** * 比较 */ public class ComparatorEx implements Comparator{ @Override public int compare(Object obj1, Object obj2) { SortEntry entry1 = (SortEntry)obj1; SortEntry entry2 = (SortEntry)obj2; switch (mSortType) { case SORT_BY_TIME: return compareByTime(entry1,entry2); case SORT_BY_READ: return compareByRead(entry1,entry2); case SORT_BY_NAME: return compareByName(entry1,entry2); default: return 0; } } /** * 鎸夐槄璇荤姸鎬佹瘮杈冩帓搴? * @param entry1 * @param entry2 * @return */ protected int compareByRead(SortEntry entry1, SortEntry entry2){ Log.v(LOGTAG,"ComparatorEx------->compareByRead"); if (entry1.mRead >= entry2.mRead){ return 1; } else{ return -1; } } protected int compareByName(SortEntry entry1, SortEntry entry2){ return 1; } protected int compareByTime(SortEntry entry1, SortEntry entry2){ return 1; } } /** * 鏍规嵁cursor鐨凱os鑾峰彇涓€涓�浉瀵圭殑绱㈠紩琛ㄤ腑瀛樺偍鐨勪綅缃? * @param position * @return */ protected int getRelativePos(int position){ int relativePos = position; switch (mSortType) { case SORT_BY_TIME: break; case SORT_BY_READ: relativePos = mSortList.get(position).mOrder; break; case SORT_BY_NAME: break; default: break; } //閫氳繃log鏌ョ湅瀵瑰簲鍏崇郴 Log.v(LOGTAG,"ThreadMsgSortCursor-->position-->" + position + " relativePos --->" + relativePos); return relativePos; } /** * 浣嶇疆鍙樻崲 鍙栧叾鍦ㄧ储寮曡〃涓��搴斾綅缃? * @param position * @return */ public boolean moveToPosition(int position){ mPos = position; //鑾峰彇鍚堥€傜殑pos int order = getRelativePos(position); return mCursor.moveToPosition(order); } public boolean moveToFirst(){ Log.v(LOGTAG,"ThreadMsgSortCursor---------->moveToFirst------"); return moveToPosition(0); } public boolean moveToLast(){ Log.v(LOGTAG,"ThreadMsgSortCursor---------->moveToLast------"); return moveToPosition(getCount() - 1); } public boolean moveToNext(){ Log.v(LOGTAG,"ThreadMsgSortCursor---------->moveToNext------"); return moveToPosition(mPos + 1); } public boolean moveToPrevious(){ Log.v(LOGTAG,"ThreadMsgSortCursor---------->moveToPrevious------"); return moveToPosition(mPos - 1); } public boolean move(int offset) { Log.v(LOGTAG,"ThreadMsgSortCursor---------->move------"); return moveToPosition(mPos + offset); } public int getPosition() { return mPos; } @Override public void close() { Log.v(LOGTAG,"ThreadMsgSortCursor---------->close------"); mCursor.close(); } }