設計模式(模板設計模式,以及Android 代碼中的應用)

一.什麼是設計模式

1.解決特定的問題,管理Activity,單例設計模式,recyclerview 添加頭部和底部,裝飾設計模式java

2.有利於代碼的規範,讓代碼更靈活android

3.有利於咱們的開發,提升開發效率。git

我的理解:玩一些套路,23種程序員

二.咱們講解一下模板設計模式

        通俗點的理解就是 :完成一件事情,有固定的數個步驟,可是每一個步驟根據對象的不一樣,而實現細節不一樣;就能夠在父類中定義一個完成該事情的總方法,按照完成事件須要的步驟去調用其每一個步驟的實現方法。每一個步驟的具體實現,由子類完成。
github


3、簡單實現的介紹

模板方法其實是封裝一個算法框架,就像是一套模板同樣。而子類能夠有不一樣的算法實現,在框架不被修改的狀況下實現算法的替換。下面咱們以開電腦這個動做來簡單演示一下模板方法。開電腦的整個過程都是相對穩定的,首先打開電腦電源,電腦檢測自身狀態沒有問題時將進入操做系統,對用戶進行驗證以後便可登陸電腦,下面咱們使用模板方法來模擬一下這個過程。
實現源碼


package com.dp.example.templatemethod;

/**
 * 抽象的Computer
 * @author wulafu
 *
 */
public abstract class AbstractComputer {

    protected void powerOn() {
        System.out.println("開啓電源");
    }

    protected void checkHardware() {
        System.out.println("硬件檢查");
    }

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

    protected void login() {
        System.out.println("小白的電腦無驗證,直接進入系統");
    }

    /**
     * 啓動電腦方法, 步驟固定爲開啓電源、系統檢查、加載操做系統、用戶登陸。該方法爲final, 防止算法框架被覆寫.
     */
    public final void startUp() {
        System.out.println("------ 開機 START ------");
        powerOn();
        checkHardware();
        loadOS();
        login();
        System.out.println("------ 開機 END ------");
    }
}


package com.dp.example.templatemethod;

/**
 * 碼農的計算機
 * 
 * @author wulafu
 */
public class CoderComputer extends AbstractComputer {
    @Override
    protected void login() {
        System.out.println("碼農只須要進行用戶和密碼驗證就能夠了");
    }
}


package com.dp.example.templatemethod;

/**
 * 軍用計算機
 * 
 * @author wulafu
 */
public class MilitaryComputer extends AbstractComputer {


    @Override
    protected void checkHardware() {
        super.checkHardware();
        System.out.println("檢查硬件防火牆");
    }

    @Override
    protected void login() {
        System.out.println("進行指紋之別等複雜的用戶驗證");
    }
}


package com.dp.example.templatemethod;

public class Test {
    public static void main(String[] args) {
        AbstractComputer comp = new CoderComputer();
        comp.startUp();

        comp = new MilitaryComputer();
        comp.startUp();

    }
}複製代碼

輸出結果以下 :
------ 開機 START ------
開啓電源
硬件檢查
載入操做系統
碼農只須要進行用戶和密碼驗證就能夠了
------ 開機 END ------
------ 開機 START ------
開啓電源
硬件檢查
檢查硬件防火牆
載入操做系統
進行指紋之別等複雜的用戶驗證
------ 開機 END ------複製代碼


經過上面的例子能夠看到,在startUp方法中有一些固定的步驟,依次爲開啓電源、檢查硬件、加載系統、用戶登陸四個步驟,這四個步驟是電腦開機過程當中不會變更的四個過程。可是不一樣用戶的這幾個步驟的實現可能各不相同,所以他們能夠用不一樣的實現。而startUp爲final方法,即保證了算法框架不能修改,具體算法實現卻能夠靈活改變。startUp中的這幾個算法步驟咱們能夠稱爲是一個套路,便可稱爲模板方法。所以,模板方法是定義一個操做中的算法的框架,而將一些步驟延遲到子類中。使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。如圖 :面試



四. Android源碼中的模式實現

在Android中,使用了模板方法且爲咱們熟知的一個典型類就是AsyncTask了,關於AsyncTask的更詳細的分析請移步Android中AsyncTask的使用與源碼分析,咱們這裏只分析在該類中使用的模板方法模式。算法

在使用AsyncTask時,咱們都有知道耗時的方法要放在doInBackground(Params... params)中,在doInBackground以前若是還想作一些相似初始化的操做能夠寫在onPreExecute方法中,當doInBackground方法執行完成後,會執行onPostExecute方法,而咱們只須要構建AsyncTask對象,而後執行execute方法便可。咱們能夠看到,它整個執行過程實際上是一個框架,具體的實現都須要子類來完成。並且它執行的算法框架是固定的,調用execute後會依次執行onPreExecute,doInBackground,onPostExecute,固然你也能夠經過onProgressUpdate來更新進度。咱們能夠簡單的理解爲以下圖的模式 :設計模式




下面咱們看源碼,首先咱們看執行異步任務的入口, 即execute方法 :數組

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    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;
    }
複製代碼


能夠看到execute方法(爲final類型的方法)調用了executeOnExecutor方法,在該方法中會判斷該任務的狀態,若是不是PENDING狀態則拋出異常,這也解釋了爲何AsyncTask只能被執行一次,所以若是該任務已經被執行過的話那麼它的狀態就會變成FINISHED。繼續往下看,咱們看到在executeOnExecutor方法中首先執行了onPreExecute方法,而且該方法執行在UI線程。而後將params參數傳遞給了mWorker對象的mParams字段,而後執行了exec.execute(mFuture)方法。
mWorker和mFuture又是什麼呢?其實mWorker只是實現了Callable接口,並添加了一個參數數組字段,咱們挨個來分析吧,跟蹤代碼咱們能夠看到,這兩個字段都是在構造函數中初始化。


public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                return postResult(doInBackground(mParams));
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    final Result result = get();

                    postResultIfNotInvoked(result);
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                } catch (Throwable t) {
                    throw new RuntimeException("An error occured while executing "
                            + "doInBackground()", t);
                }
            }
        };
    }
複製代碼

簡單的說就是mFuture就包裝了這個mWorker對象,會調用mWorker對象的call方法,而且將之返回給調用者。關於AsyncTask的更詳細的分析請移步Android中AsyncTask的使用與源碼分析,咱們這裏只分析模板方法模式。總之,call方法會在子線程中調用,而在call方法中又調用了doInBackground方法,所以doInBackground會執行在子線程。doInBackground會返回結果,最終經過postResult投遞給UI線程。 咱們再看看postResult的實現 :ruby

private Result postResult(Result result) {
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

    private static class InternalHandler extends Handler {
        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }


    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
複製代碼

能夠看到,postResult就是把一個消息( msg.what == MESSAGE_POST_RESULT)發送給sHandler,sHandler類型爲InternalHandler類型,當InternalHandler接到MESSAGE_POST_RESULT類型的消息時就會調用result.mTask.finish(result.mData[0])方法。咱們能夠看到result爲AsyncTaskResult類型,源碼以下 :

@SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
複製代碼

能夠看到mTask就是AsyncTask對象,調用AsyncTask對象的finish方法時又調用了onPostExecute,這個時候整個執行過程就完成了。 總之,execute方法內部封裝了onPreExecute, doInBackground, onPostExecute這個算法框架,用戶能夠根據本身的需求來在覆寫這幾個方法,使得用戶能夠很方便的使用異步任務來完成耗時操做,又能夠經過onPostExecute來完成更新UI線程的工做。
另外一個比較好的模板方法示例就是Activity的聲明周期函數,例如Activity從onCreate、onStart、onResume這些程式化的執行模板,這就是一個Activity的模板方法。

五. 構建整個應用的BaseActivity

接下來咱們看一下怎麼才能用到開發中... 等等我要去舉報你 --> 怎麼啦? 你抄襲別人的。怎麼?被發現了嗎,我這不是抄襲只是把好東西拿出來與你們分享而已,給你們推薦一下:github.com/simple-andr… 裏面有不少設計模式,最好你們能夠買一本書看一下這麼好的東西仍是支持一下吧但願能夠出一些更好的(雖然我看的是電子文檔)。說到這裏很是感謝你們的打賞雖然很少可是我心裏真的很感謝,但願之後能夠出更多好的文章。
  在網上查了不少關於別人講的模板設計模式,都是隻簡單的介紹一下概念而後寫一個小的事例如:程序員的一天,銀行辦理業務等等等等。我最近剃了個光頭,由於上班閒來無事看別人的博客的時候總是抓頭髮,一直都在想一想我該用到哪裏呢?哪裏又能用到呢?.......因此乾脆剃了個光頭。

由於目前仍是停留在內涵段子項目的系統架構部分,並且每一個項目確定離不開Activity,在Activity的onCreate()方法裏面基本都是:設置佈局 ->初始化頭部 -> 初始化界面 -> 初始化數據。若是按照這麼個流程是否是恰好符合咱們模板設計模式。

/** * Created by wulafu on 2019/2/8. * Description: 整個應用的BaseActivity */
public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView();
        initTitle();
        initView();
        initData();
    }

    // 初始化數據
    protected abstract void initData();

    // 初始化界面
    protected abstract void initView();

    // 設置界面視圖
    protected abstract void setContentView();

    // 初始化頭部
    protected abstract void initTitle();
}
複製代碼

之後咱們每次新建Activity就不會直接繼承自 AppCompatActivity 而是咱們本身的BaseActivity就會自動要咱們複寫BaseActivity裏面的幾個抽象方法,咱們在方法裏面寫對應的代碼。

/** * Created by wulafu on 2019/2/8. * Description: 主頁面MainActivity */
public class MainActivity extends BaseActivity {

    @Override
    protected void initData() {

    }

    @Override
    protected void initView() {

    }

    @Override
    protected void setContentView() {
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void initTitle() {

    }
}
複製代碼

這也沒見到什麼好處啊~好處我待會再來講,咱們先對BaseActivity來擴展一下,一些通用的流程和功能其實能夠放在BaseActivity裏面,好比前幾回所分享的本身動手打造一套IOC註解框架、啓動Activity...

/** * Created by wulafu on 2019/2/8. * Description: 整個應用的BaseActivity */
public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView();
        // IOC註解注入
        ViewUtils.inject(this);
        initTitle();
        initView();
        initData();
    }

    // 初始化數據
    protected abstract void initData();

    // 初始化界面
    protected abstract void initView();

    // 設置界面視圖
    protected abstract void setContentView();

    // 初始化頭部
    protected abstract void initTitle();


    /** * 啓動一個Activity * @param activity 須要啓動的Activity的Class */
    public void startActivity(Class<? extends Activity> activity) {
        Intent intent = new Intent(this,activity);
        startActivity(intent);
    }

    /** * findViewById 不須要再去強轉 */
    public <T extends View> T viewById(@IdRes int resId) {
        return (T) super.findViewById(resId);
    }
}
複製代碼

接下來咱們來講一下好處,模板設計模式的好處就不說了網上太多了,咱們就只說在真正的開發中這麼作的好處

  1. 在實際的開發過程當中咱們每每不是一我的在開發,若是把直接在onCreate()裏面寫不少東西顯然不行,可是若是寫一些方法,那麼每一個成員所寫的方法都會不一樣,況且有些哥們英語估計沒過四級,單詞都寫錯;
  2. 直接繼承自BaseActivity會自動提示覆蓋方法,更加有利於開發或者代碼的閱讀,每一個方法各盡其職只幹本身該乾的事,沒事幹就歇着;
  3. 咱們能夠把通用的流程或者是某些方法放到BaseActivity,這樣又節省代碼又方便可是不是亂七八糟的放一大堆,細節會有不少具體能夠看視頻講解;
  4. 咱們中間確定還須要邏輯層的BaseActivity,這樣作其實有利於後期的版本迭代、層次抽離和代碼重構...

六.JAVA線程中的模板設計模式


       做爲一名java程序員,建立線程是工做中常常乾的事情,也是面試中常常被提問的問題。你們都知道java中建立線程有兩種最基本的方式: 建立Thread類; 實現runnable藉口; 這兩種方式有一個共同點就是咱們的業務邏輯必需要實現run()這個方法,而後咱們調用start()方法來啓動線程。跟蹤Thread的start()方法源碼咱們能夠看到:


public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
 
        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */
        group.add(this);
 
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
複製代碼


start()方法調用了一個start0()方法,而start0()方法源碼以下:

private native void start0();
 
    /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } }複製代碼

start0是一個native修飾的方法,並且它調用了run(),其實一點從設計模式上不能理解,這裏用到的就是一個簡單的模板模式,目的就是爲了讓業務的邏輯和線程的邏輯分離。如下是模板模式模擬線程的實現,但願有助於理解模板模式在Thread中的應用


/**
 * Description
 * <p>
 * </p>
 * DATE 2019/10/1.
 *
 * @author wulafu.
 */
public class Template {
 
    public Template() {
    }
 
    public final void start(){
        System.out.println("stat方法啓動!");
        run();
        System.out.println("start方法結束!");
    }
 
    public void run(){}
 
    public static void main(String[] args) {
 
        Template template = new Template(){
            @Override
            public void run() {
                System.out.println("run方法開始運行!");
            }
        };
 
        template.start();
    }
 
複製代碼

運行結果:

stat方法啓動!run方法開始運行!start方法結束!

相關文章
相關標籤/搜索