1. 模式介绍
模式的定义
定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模式的使用场景
1.多个子类有公有的方法,并且逻辑基本相同时。
2.重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
3.重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。
2. UML类图
角色介绍
- AbstractClass : 抽象类,定义了一套算法框架。
- ConcreteClass1 : 具体实现类1;
- ConcreteClass2: 具体实现类2;
3. 模式的简单实现
简单实现的介绍
模板方法实际上是封装一个算法框架,就像是一套模板一样。而子类可以有不同的算法实现,在框架不被修改的情况下实现算法的替换。下面我们以开电脑这个动作来简单演示一下模板方法。开电脑的整个过程都是相对稳定的,首先打开电脑电源,电脑检测自身状态没有问题时将进入操作系统,对用户进行验证之后即可登录电脑,下面我们使用模板方法来模拟一下这个过程。
实现源码
package com.dp.example.templatemethod;/*** 抽象的Computer* @author mrsimple**/
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 mrsimple*/
public class CoderComputer extends AbstractComputer {@Overrideprotected void login() {System.out.println("码农只需要进行用户和密码验证就可以了");}
}package com.dp.example.templatemethod;/*** 军用计算机* * @author mrsimple*/
public class MilitaryComputer extends AbstractComputer {@Overrideprotected void checkHardware() {super.checkHardware();System.out.println("检查硬件防火墙");}@Overrideprotected 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中的这几个算法步骤我们可以称为是一个套路,即可称为模板方法。因此,模板方法是定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。如图 :
4. 构建整个应用的BaseActivity
每个项目肯定离不开Activity,在Activity的onCreate()方法里面基本都是:设置布局 ->初始化头部 -> 初始化界面 -> 初始化数据。如果按照这么个流程是不是刚好符合我们模板设计模式。
public abstract class BaseActivity extends AppCompatActivity {@Overrideprotected 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里面的几个抽象方法,我们在方法里面写对应的代码。
public class MainActivity extends BaseActivity {@Overrideprotected void initData() {}@Overrideprotected void initView() {}@Overrideprotected void setContentView() {setContentView(R.layout.activity_main);}@Overrideprotected void initTitle() {}
}
这也没见到什么好处啊~好处我待会再来说,我们先对BaseActivity来扩展一下,一些通用的流程和功能其实可以放在BaseActivity里面,比如前几次所分享的自己动手打造一套IOC注解框架、启动Activity…
public abstract class BaseActivity extends AppCompatActivity {@Overrideprotected 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);}
}
接下来我们来说一下好处,模板设计模式的好处就不说了网上太多了,我们就只说在真正的开发中这么做的好处
- 在实际的开发过程中我们往往不是一个人在开发,如果把直接在onCreate()里面写很多东西显然不行,但是如果写一些方法,那么每个成员所写的方法都会不同,何况有些哥们英语估计没过四级,单词都写错;
- 直接继承自BaseActivity会自动提示覆盖方法,更加有利于开发或者代码的阅读,每个方法各尽其职只干自己该干的事,没事干就歇着;
- 我们可以把通用的流程或者是某些方法放到BaseActivity,这样又节省代码又方便但是不是乱七八糟的放一大堆,细节会有很多具体可以看视频讲解;
- 我们中间肯定还需要逻辑层的BaseActivity,这样做其实有利于后期的版本迭代、层次抽离和代码重构…