主要內容:
一、裝飾模式定義
裝飾模式定義: |
Attach additional responsibilities to an object dynamically keeping the same interface. Decoators provide a flexible alternative to subclassing for extending functionality. |
動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更為靈活。 |
如上圖所示(截取自《Head First Design Patterns》一書),主要包括四個部分:
1. Component抽象組件,是一個接口或者是抽象類,就是定義我們最核心的對象,也就是最原始的對象。(注:在裝飾模式中,必然有一個最基本、最核心、最原始的接口或者抽象類充當Component抽象組件)
2. ConcreteComponent具體組件,是最核心、最原始、最基本的接口或抽象類的實現,我們需要裝飾的就是它。
3. Decorator裝飾角色, 一般是一個抽象類,實現接口或者抽象方法,它的屬性里必然有一個private變量指向Component抽象組件。
4. 具體裝飾角色,如上圖中的ConcreteDecoratorA和ConcreteDecoratorB,我們要把我們最核心的、最原始的、最基本的東西裝飾成其它東西。
二、裝飾模式的優勢
裝飾類和被裝飾類可以獨立發展,而不會相互耦合。換句話說,Component類無須知道Decorator類,Decorator類是從外部來擴展Component類的功能,而Decorator也不用知道具體的組件。 |
裝飾模式是繼承關系的一個替代方案。我們看裝飾類Decorator,不管裝飾多少層,返回的對象還是Component,實現的還是is-a的關系。 |
裝飾模式可以動態地擴展一個實現類的功能。 |
三、裝飾模式在Android源碼中的應用
在Android源碼中,其中一個比較經典的使用到裝飾模式的就是由Context抽象類擴展出的ContextWrapper的設計。繼承結構如下圖所示:
1. Context就是我們的抽象組件,它提供了應用運行的基本環境,是各組件和系統服務通信的橋梁,隱藏了應用與系統服務通信的細節,簡化了上層應用的開發。所以Contex就是“裝飾模式”里的Component。
2. Context類是個抽象類,android.app.ContextImpl派生實現了它的抽象接口。ContextImpl對象會與Android框架層的各個服務(包括組件管理服務、資源管理服務、安裝管理服務等)建立遠程連接,通過對Android進程間的通信機制(IPC)和這些服務進行通信。所以ContextImpl就是“裝飾模式”里的ConcreteComponent。
3. 如果上層應用期望改變Context接口的實現,就需要使用android.content.ContextWrapper類,它派生自Context,其中具體實現都是通過組合的方式調用ContextImpl類的實例(在ContextWrapper中的private屬性mBase)來完成的。這樣的設計,使得ContextImpl與ContextWrapper子類的實現可以單獨變化,彼此獨立。所以可以看出ContextWrapper就是“裝飾模式”里的Decorator。
4. Android的界面組件Activity、服務組件Service以及應用基類Application都派生於ContextWrapper,它們可以通過重載來修改Context接口的實現。所以可以看出Activity、服務組件Service以及應用基類Application就是“裝飾模式”里的具體裝飾角色A、B、C。
注:上圖可以看出界面組件基類android.app.Activity添加了界面繪制相關的實現,增加了處理界面事件的相關接口。它存放界面中各控件的對象,並與窗口管理服務建立連接,傳遞界面相關的事件和操作。
現在開始看看源代碼是怎么進行組織使用“裝飾模式”的。
1. Context抽象類:在該抽象類中定義了一系列get***()和set***()等抽象函數,其中有一個沒有實現的startActivity抽象函數:
/** * Interface to global information about an application environment. This is * an abstract class whose implementation is provided by * the Android system. It * allows access to application-specific resources and classes, as well as * up-calls for application-level operations such as launching activities, * broadcasting and receiving intents, etc. */ public abstract class Context { /** * File creation mode: the default mode, where the created file can only * be accessed by the calling application (or all applications sharing the * same user ID). * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE */ 。。。。。。 /** * Launch a new activity. You will not receive any information about when * the activity exits. * * <p>Note that if this method is being called from outside of an * {@link android.app.Activity} Context, then the Intent must include * the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag. This is because, * without being started from an existing Activity, there is no existing * task in which to place the new activity and thus it needs to be placed * in its own separate task. * * <p>This method throws {@link ActivityNotFoundException} * if there was no Activity found to run the given Intent. * * @param intent The description of the activity to start. * * @throws ActivityNotFoundException * * @see PackageManager#resolveActivity */ public abstract void startActivity(Intent intent); ......
2. 看android.app.ContextImpl類,找到startActivity方法,看它的實現:
/** * Common implementation of Context API, which provides the base * context object for Activity and other application components. */ class ContextImpl extends Context { private final static String TAG = "ApplicationContext"; private final static boolean DEBUG = false; private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs = new HashMap<String, SharedPreferencesImpl>(); 。。。。。。 @Override public void startActivity(Intent intent) { if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity)null, intent, -1); } .....
這里不在闡述是如何實現的,但至少可以看出的ContextImpl是實現了Context的抽象方法startActivity函數。
3. 現在來看裝飾類ContextWrapper如何來調用這個startActivity方法的:
/** * Proxying implementation of Context that simply delegates all of its calls to * another Context. Can be subclassed to modify behavior without changing * the original Context. */ public class ContextWrapper extends Context { Context mBase; public ContextWrapper(Context base) { mBase = base; }
3.1 首先必須包含屬性Context抽象類的實例對象mBase。
@Override public void startActivity(Intent intent) { mBase.startActivity(intent); }
3.2 看出它只是單純的調用父類Context的方法mBase.startActivity(intent),並未做修改。
4. 看看具體裝飾類如何來裝飾和擴展父類ContextWrapper的:
Activity類:

public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 { private static final String TAG = "Activity"; /** Standard activity result: operation canceled. */ public static final int RESULT_CANCELED = 0; /** Standard activity result: operation succeeded. */ public static final int RESULT_OK = -1; /** Start of user-defined activity results. */ public static final int RESULT_FIRST_USER = 1; private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState"; private static final String FRAGMENTS_TAG = "android:fragments"; private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds"; private static final String SAVED_DIALOGS_TAG = "android:savedDialogs"; private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_"; private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_"; private static class ManagedDialog { Dialog mDialog; Bundle mArgs; } private SparseArray<ManagedDialog> mManagedDialogs; // set by the thread after the constructor and before onCreate(Bundle savedInstanceState) is called. private Instrumentation mInstrumentation; private IBinder mToken; private int mIdent; /*package*/ String mEmbeddedID; private Application mApplication; /*package*/ Intent mIntent; private ComponentName mComponent; /*package*/ ActivityInfo mActivityInfo; /*package*/ ActivityThread mMainThread; Activity mParent; boolean mCalled; boolean mCheckedForLoaderManager; boolean mLoadersStarted; /*package*/ boolean mResumed; private boolean mStopped; boolean mFinished; boolean mStartedActivity; /** true if the activity is going through a transient pause */ /*package*/ boolean mTemporaryPause = false; /** true if the activity is being destroyed in order to recreate it with a new configuration */ /*package*/ boolean mChangingConfigurations = false; /*package*/ int mConfigChangeFlags; /*package*/ Configuration mCurrentConfig; private SearchManager mSearchManager; private MenuInflater mMenuInflater; static final class NonConfigurationInstances { Object activity; HashMap<String, Object> children; ArrayList<Fragment> fragments; SparseArray<LoaderManagerImpl> loaders; } /* package */ NonConfigurationInstances mLastNonConfigurationInstances; private Window mWindow; private WindowManager mWindowManager; /*package*/ View mDecor = null; /*package*/ boolean mWindowAdded = false; /*package*/ boolean mVisibleFromServer = false; /*package*/ boolean mVisibleFromClient = true; /*package*/ ActionBarImpl mActionBar = null; private CharSequence mTitle; private int mTitleColor = 0; final FragmentManagerImpl mFragments = new FragmentManagerImpl(); SparseArray<LoaderManagerImpl> mAllLoaderManagers; LoaderManagerImpl mLoaderManager; private static final class ManagedCursor { ManagedCursor(Cursor cursor) { mCursor = cursor; mReleased = false; mUpdated = false; } private final Cursor mCursor; private boolean mReleased; private boolean mUpdated; } private final ArrayList<ManagedCursor> mManagedCursors = new ArrayList<ManagedCursor>(); // protected by synchronized (this) int mResultCode = RESULT_CANCELED; Intent mResultData = null; private boolean mTitleReady = false; private int mDefaultKeyMode = DEFAULT_KEYS_DISABLE; private SpannableStringBuilder mDefaultKeySsb = null; protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused}; private final Object mInstanceTracker = StrictMode.trackActivity(this); private Thread mUiThread; final Handler mHandler = new Handler(); /** Return the intent that started this activity. */ public Intent getIntent() { return mIntent; } /** * Change the intent returned by {@link #getIntent}. This holds a * reference to the given intent; it does not copy it. Often used in * conjunction with {@link #onNewIntent}. * * @param newIntent The new Intent object to return from getIntent * * @see #getIntent * @see #onNewIntent */ public void setIntent(Intent newIntent) { mIntent = newIntent; } /** Return the application that owns this activity. */ public final Application getApplication() { return mApplication; } /** Is this activity embedded inside of another activity? */ public final boolean isChild() { return mParent != null; } /** Return the parent activity if this view is an embedded child. */ public final Activity getParent() { return mParent; } /** Retrieve the window manager for showing custom windows. */ public WindowManager getWindowManager() { return mWindowManager; } /** * Retrieve the current {@link android.view.Window} for the activity. * This can be used to directly access parts of the Window API that * are not available through Activity/Screen. * * @return Window The current window, or null if the activity is not * visual. */
不斷擴展自己的屬性和方法;
同樣的Service類也是:
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 { private static final String TAG = "Service"; public Service() { super(null); } /** Return the application that owns this service. */ public final Application getApplication() { return mApplication; } /** * Called by the system when the service is first created. Do not call this method directly. */ public void onCreate() { } /** * @deprecated Implement {@link #onStartCommand(Intent, int, int)} instead. */ @Deprecated public void onStart(Intent intent, int startId) { } /** * Bits returned by {@link #onStartCommand} describing how to continue * the service if it is killed. May be {@link #START_STICKY}, * {@link #START_NOT_STICKY}, {@link #START_REDELIVER_INTENT}, * or {@link #START_STICKY_COMPATIBILITY}. */ public static final int START_CONTINUATION_MASK = 0xf; /** * Constant to return from {@link #onStartCommand}: compatibility * version of {@link #START_STICKY} that does not guarantee that * {@link #onStartCommand} will be called again after being killed. */ public static final int START_STICKY_COMPATIBILITY = 0; /** * Constant to return from {@link #onStartCommand}: if this service's * process is killed while it is started (after returning from * {@link #onStartCommand}), then leave it in the started state but * don't retain this delivered intent. Later the system will try to * re-create the service. Because it is in the started state, it will * guarantee to call {@link #onStartCommand} after creating the new * service instance; if there are not any pending start commands to be * delivered to the service, it will be called with a null intent * object, so you must take care to check for this. * * <p>This mode makes sense for things that will be explicitly started * and stopped to run for arbitrary periods of time, such as a service * performing background music playback. */ public static final int START_STICKY = 1; /** * Constant to return from {@link #onStartCommand}: if this service's * process is killed while it is started (after returning from * {@link #onStartCommand}), and there are no new start intents to * deliver to it, then take the service out of the started state and * don't recreate until a future explicit call to * {@link Context#startService Context.startService(Intent)}. The * service will not receive a {@link #onStartCommand(Intent, int, int)} * call with a null Intent because it will not be re-started if there * are no pending Intents to deliver. * 。。。。。。
Application類同樣:
public class Application extends ContextWrapper implements ComponentCallbacks2 { private ArrayList<ComponentCallbacks> mComponentCallbacks = new ArrayList<ComponentCallbacks>(); private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks = new ArrayList<ActivityLifecycleCallbacks>(); /** @hide */ public LoadedApk mLoadedApk; public interface ActivityLifecycleCallbacks { void onActivityCreated(Activity activity, Bundle savedInstanceState); void onActivityStarted(Activity activity); void onActivityResumed(Activity activity); void onActivityPaused(Activity activity); void onActivityStopped(Activity activity); void onActivitySaveInstanceState(Activity activity, Bundle outState); void onActivityDestroyed(Activity activity); } public Application() { super(null); }
最后讓我們記住支撐“裝飾模式”的設計原則:
Classes should be open for extension, but closed for modification.
自己可以看操作文件的Java源代碼,設計思想也是應用到“裝飾模式”,祝大家元旦快樂~~~