1. 程式人生 > >工廠模式和抽象工廠模式以及在Android中的應用

工廠模式和抽象工廠模式以及在Android中的應用

《Android原始碼設計模式解析與實戰》第5章第6章讀書筆記

工廠方法模式介紹

工廠方法模式(Factory Pattern)建立型設計模式之一,在平時開發中或多或少都會使用它,如Android中的Activity裡的各個生命週期方法,以onCreate方法為例,它就可以看作是一個工廠方法,在其中我們將構造的View通過setContentView返回給framework處理,剩下的佈局就由系統去建立。總結來說,定義一個用於建立物件的介面,讓子類決定例項化哪個類。它的使用場景是任何需要生成複雜物件的地方,都可以使用工廠方法模式。複雜物件使用工廠模式,用new就可以完成建立的物件無需使用工廠模式。

工廠方法模式uml類圖

這裡寫圖片描述

工廠模式通用模板程式碼:

abstract class Product{

    /**
     *  抽象的產品方法,具體方法由子類去實現
     */
    abstract fun method()

}

class ConcreteProductA : Product(){

    override fun method() {
        println("具體產品類A")
    }
}

class ConcreteProductB : Product(){

    override fun method() {
        println("具體產品類B"
) } } /** * 抽象工廠類,生產什麼產品由子類去實現 */ abstract class Factory{ abstract fun createProduct(): Product } class ConcreteFactory: Factory(){ override fun createProduct(): Product = ConcreteProductA() } //Test @Test fun demo1(){ val factory = ConcreteFactory() val product = factory.createProduct() product.method() //具體產品類A
}

上述程式碼中構造的工廠物件,通過其生產的產品物件,得到的產品物件是ConcreteProductA,如果想得到產品物件B,直接更換就好了。

還有一種比較常見的是利用反射更加簡潔的生產具體物件

/**
 * 反射版本
 */
abstract class FactoryWithReflect{

    abstract fun <T:Product> createProduct(clz: Class<T>): T?

}

class ConcreteFactoryWithReflect: FactoryWithReflect(){

    override fun <T : Product> createProduct(clz: Class<T>): T? {
        var product: T? = null
        try {
            product = Class.forName(clz.name).newInstance() as T
        }catch (e:Exception){
            e.printStackTrace()
        }
        return product
    }
}

//test
@Test
fun demo2(){
    val factory = ConcreteFactoryWithReflect()
    val product = factory.createProduct(ConcreteProductA::class.java)
    product?.method()
}

根據第二版本看出,想要什麼樣的例項,只需要傳入對應的class就可以了,這種方法比較簡潔,動態。當然也可以為每一個產品建立一個具體的工廠。

工廠方法模式在android中的應用

拿在本文開頭onCreate舉例,它是Activity的一個入口點,接下來就看下如何在Android系統中呼叫的onCreate 方法。對於一個應用程式來說,其真正的入口是ActivityThread類裡面的main方法,ActivityThread是一個final類,不能被繼承,當Zygote程序孵化出一個新的應用程序之後,會執行ActivityThreadmain方法。

public static void main(String[] args) {
 ...
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
...
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

main方法裡面做了一些常規邏輯,比如準備Looper和訊息佇列,然後呼叫ActivityThreadattach方法將其繫結到ActivityManagerService中,接著就是不斷的讀取訊息佇列中的訊息並分發訊息(事件驅動模型)

看下attach方法

private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
    ...
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    ...
    }
    ...
}

通過system來區分是系統還是普通應用,我們只看普通應用,ActivityManager.getService() 返回的就是一個AMS物件,由於AMS是系統服務,所以它們之間需要程序通訊,接著呼叫attachApplication,來將mAppThread 交給AMS 處理,看下attachApplication方法

//AMS
@Override
public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid);
        Binder.restoreCallingIdentity(origId);
    }
}

接著轉向呼叫attachApplicationLocked

    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
...            
        try {
...
                thread.bindApplication(processName, appInfo, providers,
                        app.instr.mClass,
                        profilerInfo, app.instr.mArguments,
                        app.instr.mWatcher,
                        app.instr.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial); //1
...                        
        } catch (Exception e) {
...
            return false;
        }
...
        mProcessesOnHold.remove(app);

        boolean badApp = false;
        boolean didSomething = false;

        if (normalMode) {
            try {
                if (mStackSupervisor.attachApplicationLocked(app)) { //2
                    didSomething = true;
                }
            } catch (Exception e) {
                badApp = true;
            }
        }
...
        return true;
    }

這個方法邏輯很長,簡化了不必要的邏輯之後,只剩下兩個主要的方法bindApplicationattachApplicationLocked,第一個方法引數很多,如其名字一樣,將ApplicationThread物件繫結到AMS中。而第二個方法mStackSupervisor指向一個ActivityStackSupervisor,而attachApplicationLocked方法描述如下

    boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = false;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                if (!isFocusedStack(stack)) {
                    continue;
                }
                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
                final ActivityRecord top = stack.topRunningActivityLocked();
                final int size = mTmpActivityList.size();
                for (int i = 0; i < size; i++) {
                    final ActivityRecord activity = mTmpActivityList.get(i);
                    if (activity.app == null && app.uid == activity.info.applicationInfo.uid
                            && processName.equals(activity.processName)) {
                        try {
                            if (realStartActivityLocked(activity, app,
                                    top == activity /* andResume */, true /* checkConfig */)) {  
                                didSomething = true;
                            }
                        } catch (RemoteException e) {
                       ...
                            throw e;
                        }
                    }
                }
            }
        }
        if (!didSomething) {
            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
        }
        return didSomething;
    }

方法的程式碼比較長,而主要邏輯是

if (realStartActivityLocked(activity, app, top == activity /*andResume */, true /* checkConfig */)) {  
     didSomething = true;
}

這裡就是真正啟動Activity的邏輯

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {

...
//所有引數資訊準備好之後,就可以真正啟動Activity了
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
       System.identityHashCode(r), r.info, 
       mergedConfiguration.getGlobalConfiguration(),
       mergedConfiguration.getOverrideConfiguration(), r.compat,
       r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
       r.persistentState, results, newIntents, !andResume,
       mService.isNextTransitionForward(), profilerInfo);

...

}

這個方法中會準備Activity的引數資訊,準備完畢之後呼叫ApplicationThreadscheduleLaunchActivity方法啟動Activty,

@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
        int procState, Bundle state, PersistableBundle persistentState,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

    updateProcessState(procState, false);
 //將引數封裝到一個ActivityClientRecord物件中
    ActivityClientRecord r = new ActivityClientRecord();

    r.token = token;
    r.ident = ident;
    r.intent = intent;
    r.referrer = referrer;
    r.voiceInteractor = voiceInteractor;
    r.activityInfo = info;
    r.compatInfo = compatInfo;
    r.state = state;
    r.persistentState = persistentState;

    r.pendingResults = pendingResults;
    r.pendingIntents = pendingNewIntents;

    r.startsNotResumed = notResumed;
    r.isForward = isForward;

    r.profilerInfo = profilerInfo;

    r.overrideConfig = overrideConfig;
    updatePendingConfiguration(curConfig);

    sendMessage(H.LAUNCH_ACTIVITY, r);
}

這個方法中首先構造一個ActivityClientRecord 物件,設定相關引數,最後通過sendMessage 方法傳送一個啟動Activity的訊息,由ActivityThreadHandler啟動,在ActivityThread 中維護了一個Handler的例項H,看下它是怎麼處理H.LAUNCH_ACTIVITY 這樣的標誌訊息的

    private class H extends Handler {
        public static final int LAUNCH_ACTIVITY         = 100;
...
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");

                } break;
        }
    }

它會呼叫handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); 方法,而在這個方法中會呼叫performLaunchActivity 方法,這個方法裡面是處理具體Activity的啟動邏輯

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //獲取ActivityInfo 
    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        //獲取packageInfo
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                Context.CONTEXT_INCLUDE_CODE);
    }
    //獲取ComponentName   
    ComponentName component = r.intent.getComponent();
    if (component == null) {
        component = r.intent.resolveActivity(
            mInitialApplication.getPackageManager());
        r.intent.setComponent(component);
    }

    if (r.activityInfo.targetActivity != null) {
        component = new ComponentName(r.activityInfo.packageName,
                r.activityInfo.targetActivity);
    }

    ContextImpl appContext = createBaseContextForActivity(r);
    //通過Instrumentation構造Activity物件並設定引數
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
...
    }

    try {
        //獲取Application物件
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
        if (activity != null) {
...
            if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                //構造window物件
                window = r.mPendingRemoveWindow;
                r.mPendingRemoveWindow = null;
                r.mPendingRemoveWindowManager = null;
            }
            appContext.setOuterContext(activity);
            //將相關引數繫結到activity
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);

            if (customIntent != null) {
                activity.mIntent = customIntent;
            }
            r.lastNonConfigurationInstances = null;
            checkAndBlockForNetworkAccess();
            activity.mStartedActivity = false;
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                activity.setTheme(theme);
            }

            activity.mCalled = false;
            //呼叫Activity的onCreate方法
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);   //1
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
   ...
        }
        r.paused = true;

        mActivities.put(r.token, r);

    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
      ...
    }

    return activity;
}

註釋一處會呼叫callActivityOnCreate 方法

public void callActivityOnCreate(Activity activity, Bundle icicle,
        PersistableBundle persistentState) {
    prePerformCreate(activity);
    activity.performCreate(icicle, persistentState);
    postPerformCreate(activity);
}

而在performCreate 中最終會呼叫onCreate方法

final void performCreate(Bundle icicle) {
    performCreate(icicle, null);
}

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
    mCanEnterPictureInPicture = true;
    restoreHasCurrentPermissionRequest(icicle);
    if (persistentState != null) {
        onCreate(icicle, persistentState);
    } else {
    //最終執行onCreate方法的呼叫
        onCreate(icicle);
    }
   ...
}

到這裡,基本就明白了整個Activity的啟動流程到onCreate方法的呼叫,而對於外部呼叫者來說,裡面做的什麼操作基本都是不知道的,只是提供onCreate這一系列生命週期方法,這就是典型的工廠模式設計模式

為了看得更清楚,畫了一個時序圖,方便理解

這裡寫圖片描述

總結來說,工廠設計模式可以簡化類的建立過程,但是也導致類的結構複雜化了,所以是否要選擇工廠設計模式,需要權衡利弊了。

抽象工廠模式介紹

抽象工廠模式(Abstract Factory Pattern)也是建立型設計模式之一,起源於以前對不同作業系統圖形化解決方案,如不同作業系統對按鈕和文字控制元件的實現不同,展示效果也不同。

定義:為建立一組相關或者是相互依賴的物件提供一個介面,而不需要指定它們具體的類。

使用場景:一個物件族有相同的約束時可以使用抽象工廠模式。比如Android,IOS,Window Phone下都有簡訊軟體和撥號軟體,兩者都是相同的功能,但是在不同作業系統平臺下,其程式碼的實現邏輯也是不相同的,這時候就可以採用抽象工廠模式。還有一個就是換面板功能,一整套一起換

抽象工廠模式uml類圖

這裡寫圖片描述

抽象工廠模式的模板程式碼

/** 
 *  抽象產品A
 */
abstract class AbstractProductA{
    /**
     * 抽象方法
     */
    abstract fun method()

}

/**
 * 具體產品A1
 */
class ConcreteProductA1: AbstractProductA(){

    override fun method() {
        println("具體產品A1")
    }
}

/**
 * 具體產品A2
 */
class ConcreteProductA2: AbstractProductA(){

    override fun method() {
        println("具體產品A2")
    }
}

/**
 * 抽象產品B
 */
abstract class AbstractProductB{
    /**
     * 抽象方法
     */
    abstract fun method()

}

/**
 *  具體產品B1
 */
class ConcreteProductB1: AbstractProductB(){

    override fun method() {
        println("具體產品B1")
    }
}

/**
 *  具體產品B2
 */
class ConcreteProductB2: AbstractProductB(){

    override fun method() {
        println("具體產品B2")
    }
}

/**
 *  抽象工廠
 */
abstract class AbstractFactory{
    /**
     *  生產產品A
     */
    abstract fun createProductA(): AbstractProductA

    /**
     *  生產產品B
     */
    abstract fun createProductB(): AbstractProductB

}

/**
 *  具體工廠1
 */
class ConcreteFactory1: AbstractFactory(){

    override fun createProductA(): AbstractProductA = ConcreteProductA1()

    override fun createProductB(): AbstractProductB = ConcreteProductB1()
}

/**
 *  具體工廠2
 */
class ConcreteFactory2: AbstractFactory(){

    override fun createProductA(): AbstractProductA = ConcreteProductA2()

    override fun createProductB(): AbstractProductB = ConcreteProductB2()
}

角色介紹:

  • AbstractProduct: 抽象產品角色,它為每種產品申明介面

  • ConcreteProduct:具體產品角色,它定義具體工廠生產的具體產品物件。

  • AbstractFactory:抽象工廠,負責宣告建立一種產品的方法。

  • ConcreteFactory:具體工廠,實現了抽象工廠中建立產品的方法。

抽象工廠模式在android中的應用

在平時的開發過程中很少用到抽象工廠模式,一個重要原因就是略顯複雜,對於Android開發者來說,一個比較適合的應用就是主題的修改,下面就模擬一套亮色主題LightTheme和暗色主題DarkTheme,而在這兩種主題下有各自的UI元素,這種時候就可以使用抽象工廠模式

/**
 *  抽象主題按鈕
 */
abstract class ThemeButton(context: Context) : Button(context){

    init {
        initTextColor()
    }

    /**
     *  初始化文字顏色
     */
    abstract fun initTextColor()

}

/**
 * 暗色按鈕
 */
class DarkButton(context: Context): ThemeButton(context){

    override fun initTextColor() {
        setTextColor(R.color.dartColor)
    }
}

/**
 *  亮色按鈕
 */
class LightButton(context: Context): ThemeButton(context){

    override fun initTextColor() {
        setTextColor(R.color.lightColor)
    }
}

/**
 *  抽象主題工廠類
 */
abstract class AbstractThemeFactory(val context: Context){

    abstract fun createButton(): ThemeButton

}

/**
 *  暗色按鈕工廠
 */
class DarkThemeFactory(context: Context): AbstractThemeFactory(context){

    override fun createButton(): ThemeButton = DarkButton(context)
}

/**
 *  亮色按鈕工廠
 */
class LightThemeFactory(context: Context): AbstractThemeFactory(context){

    override fun createButton(): ThemeButton = LightButton(context)
}

//Test
 */
@RunWith(AndroidJUnit4::class)
class AbstractFactoryPatterThemeTest {

    @Test
    fun test() {

        val factoryDark = DarkThemeFactory(InstrumentationRegistry.getTargetContext())
        println("暗色:"+factoryDark.createButton())

        val factoryLight = LightThemeFactory(InstrumentationRegistry.getTargetContext())
        println("亮色:"+factoryLight.createButton())

    }

}

result:
暗色:com.microcity.myapplication.DarkButton{8cd843f VFED..C.. ......I. 0,0-0,0}
亮色:com.microcity.myapplication.LightButton{5495f0d VFED..C.. ......I. 0,0-0,0}

在上面的例子中定義了一個主題按鈕ThemeButton 抽象類,接著實現了DarkButton 暗色和LightButton亮色兩種按鈕。然後定義了一個生產ThemeButton 的工廠類,接著實現了DarkThemeFactoryLightThemeFactory 兩種主題的工廠類,分別用來實現不同主題的ThemeButton,這就是大致的邏輯。

注意:雖然這種模式簡化了類之間的分工合作,但是無疑增加了大量的類,是否要使用這種設計模式,還是要攢橫下利弊的,因為如果新增加了一個UI控制元件的時候,那麼抽象的類都需要修改,並且具體的實現類也需要修改,這樣修改起來還是很麻煩的。

抽象工廠模式的優缺點

  • 優點:

分離介面與實現,客戶端使用抽象工廠來建立需要的物件,而不需要知道具體的實現是誰,只是面向產品的介面程式設計,使其從具體的產品實現中解耦,同時基於介面與實現的分離,使抽象該工廠方法模式在切換產品類時候更加靈活,容易。

  • 缺點:

第一個就是類檔案的爆炸性增加,二是不容易擴充套件新的產品類,每當增加一個產品類就需要修改抽象工廠,那麼所有具體的工廠類均會被修改。

參考

1 .《Android原始碼設計模式解析與實戰》
2 . 抽象工廠模式