设计模式(模板设计模式,以及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方法结束!

相关文章
相关标签/搜索