設計模式(十二)模板設計模式


更多設計模式文章請閱讀:

設計模式專欄

1.定義:

定義一個操作中的算法框架,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟

2.使用場景

1.多個子類有公有的方法,並且邏輯基本相同
2.重要、複雜的算法,可以把核心算法設計爲模板方法,周邊的相關細節功能則由各個子類實現
3.重構時,模板方法模式是一個經常使用的模式,把相同的代碼抽取到父類中,然後通過鉤子函數約束其行爲

3.UML建模圖

在這裏插入圖片描述

AbsTemplate:抽象類,定義了一套算法框架
ConcreteImplA: 具體實現類A
ConcreteImplB: 具體實現類B

4.模板方法模式簡單實現

以電腦開機到關機整個過程作爲模板,這裏麪包括如下過程,開機–>加載系統–>用戶登錄—>關機
而具體的針對2中不同的電腦,Windows和MAC,這裏定義Windows用戶登錄是輸入用戶名和密碼,而MAC用戶登錄則是驗證指紋,所以,整個核心流程是相同的,但是2中操作系統的用戶登錄的方法不同,即:

  • 電腦抽象類
public abstract class AbstractComputer {
    /**
     * 開機
     */
    protected void powerOn(){
        System.out.println("開啓電源");

    }

    protected void loadOS(){
        System.out.println("加載操作系統");
    }


    protected void login(){
        System.out.println("小白進入系統,無需登錄");
    }


    //注意這裏使用了final防止子類修改算法步驟
    public final void startUp(){
        System.out.println("===開機===");
        powerOn();
        loadOS();
        login();
        System.out.println("===關機===");
    }
}
  • Windows電腦實現
public class WinComputer extends AbstractComputer {

    @Override
    protected void login() {
        System.out.println("windows輸入用戶名和密碼進入系統了");
    }
}
  • MAC電腦實現

public class MacComputer extends AbstractComputer {

    @Override
    protected void login() {
        System.out.println("mac驗證指紋識別進入系統了");
    }
}
  • 最終測試
public class Test {

    public static void main(String[] args) {
        AbstractComputer winComputer=new WinComputer();
        winComputer.startUp();

        AbstractComputer macComputer=new MacComputer();
        macComputer.startUp();
    }
}
  • 測試結果
    在這裏插入圖片描述

5.模板方法在Android中的使用

相信做Android開發的同學,在前幾年還沒有出現這麼多好用的框架的時候,網絡請求或者大量IO流操作一定離不開AsyncTask,它給我們提供了非常簡便的用法,個人感覺是Rxjava的前身,該類則通過依次執行onPreExecute、doInBackground、onPostExecute的算法框架,來實現網絡請求或者IO操作等。

  • 簡單看一下AsyncTask的源碼
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
  • 在execute()方法中看到執行了executeOnExecutor(sDefaultExecutor, params);方法,在跟進查看executeOnExecutor()方法如下:
    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }
  • 從這裏可以看到首先執行了onPreExecute();然後將參數封裝給mWorker對象和mFuture對象,之後調用了exec.execute(mFuture),開始執行真正的耗時操作。
    在這裏插入圖片描述
public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

通過查看Executor源碼,可以看到execute()方法中的參數爲Runnable類型,即mFuture爲Runnable實現類,即done()方法可以理解爲run()方法,又因爲FutureTask的參數爲mWorker,則間接執行到mWorker中的方法即doInbackground()方法,即處理耗時操作,將返回的結果,通過postResultIfNotInvoked(get())方法,通過handler切換到主線程。

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

簡單的說:mFuture包裝了mWorker對象,然後通過調用mFuture中的run()方法,簡介調用了mWorker中的call方法,在call方法中又調用了doInBackground()方法,因爲mFuture提交了線程池來執行,所以doInBackground()方法執行在非UI線程中,得到doInbackground的結果後,通過postResult傳遞出結果給UI線程

6.總結

execute方法中封裝了onPreExecute()、doInbackground()、onPostExecute()模板算法,用戶根據自己的需求,複寫相關方法,使得用戶很方便的使用異步任務來完成耗時的操作及更新UI,其實是通過線程池來執行耗時的任務,得到結果後,通過handler將結果傳遞到UI線程來執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章