介绍
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
何时使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏差异。 2、西游记里面菩萨定好的 81难,这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。
模板模式中的方法
模板方法中的方法可以分为两大类:模板方法和基本方法。
模板方法
-
一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。
-
一个抽象类可以有任意多个模板方法,而不限于一个。每一个模板方法都可以调用任意多个具体方法。
基本方法
基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook
Method)。
- 抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。
- 具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换。
- 钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现。
实现
今天就以博主放假在家的糜烂生活为例子吧!每天早上10点起床,每天吃一样的早饭,吃完早饭打游戏都可能不一样,打完游戏吃每天都一样的午饭,吃完午饭再看情况要不要睡午觉,一般情况下都是不睡的!
每天都过得差不多,这就是我的假期模板。于是,我们创建一个HolidayTemplate
public abstract class HolidayTemplate {
public final void holiday() {
getUp();
getBreakfast();
plagGame();
getLunch();
sleep();
}
private void getUp() {
System.out.println("起床床~");
}
private void getBreakfast() {
System.out.println("吃早饭饭~");
}
protected abstract void plagGame();
private void getLunch() {
System.out.println("吃午饭饭~");
}
protected void sleep() {
}
}
以上就是我一天的模板了,昨天呢我玩的是LOL,睡午10小时,那么现在来创建我昨天的一个子类吧
public class Yesterday extends HolidayTemplate {
@Override
protected void plagGame() {
System.out.println("英雄联盟游戏中:fuck,青铜局都能有演员吗?");
}
@Override
protected void sleep() {
System.out.println("午睡10小时,886");
}
}
public class Main {
public static void main(String[] args) {
Yesterday yesterday = new Yesterday();
yesterday.holiday();
}
}
控制台打出:
起床床~
吃早饭饭~
英雄联盟游戏中:fuck,青铜局都能有演员吗?
吃午饭饭~
午睡10小时,886
此处说明一下例子中的方法,仔细看起床、吃早饭、吃午饭这几个方法使用private修饰符修饰,因为我每天都必须起床、吃早饭、吃午饭,不允许有一天不这么做,也就是不允许子类修改这些行为;而打游戏使用了abstract,因为我每天都一定要玩游戏,但是又不确定该玩什么,于是子类就需要实现这个方法;最后看一下睡觉这个方法,因为我不一定每天都睡午觉,所以在父类中是一个空实现,如果哪天想睡午觉了,重写这个方法就可以了。
项目中的实际应用
一、servlet
以下是HttpServlet父类的一个service方法,充当模板方法模式中的模板方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
熟悉JavaWeb的选手应该知道在编写一个servlet的时候,需要重写两个重要的方法doGet()和doPost(),
这两个方法就相当于是前面例子中的“睡午觉”,是一个钩子方法。
二、安卓MVVM框架MVVMHabit
熟悉安卓的选手都知道,安卓在新创建一个Activity会调用Activity中的onCreate()方法,以下贴上MVVMHabit中重写过的BaseActivity。(第一部分是完整的代码,我会分开讲解代码)
public abstract class BaseActivity<V extends ViewDataBinding, VM extends BaseViewModel> extends RxAppCompatActivity implements IBaseView {
protected V binding;
protected VM viewModel;
private int viewModelId;
private MaterialDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//页面接受的参数方法
initParam();
//私有的初始化Databinding和ViewModel方法
initViewDataBinding(savedInstanceState);
//私有的ViewModel与View的契约事件回调逻辑
registorUIChangeLiveDataCallBack();
//页面数据初始化方法
initData();
//页面事件监听的方法,一般用于ViewModel层转到View层的事件注册
initViewObservable();
//注册RxBus
viewModel.registerRxBus();
}
@Override
protected void onDestroy() {
super.onDestroy();
//解除Messenger注册
Messenger.getDefault().unregister(viewModel);
if (viewModel != null) {
viewModel.removeRxBus();
}
if(binding != null){
binding.unbind();
}
}
/**
* 注入绑定
*/
private void initViewDataBinding(Bundle savedInstanceState) {
//DataBindingUtil类需要在project的build中配置 dataBinding {enabled true }, 同步后会自动关联android.databinding包
binding = DataBindingUtil.setContentView(this, initContentView(savedInstanceState));
viewModelId = initVariableId();
viewModel = initViewModel();
if (viewModel == null) {
Class modelClass;
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[1];
} else {
//如果没有指定泛型参数,则默认使用BaseViewModel
modelClass = BaseViewModel.class;
}
viewModel = (VM) createViewModel(this, modelClass);
}
//关联ViewModel
binding.setVariable(viewModelId, viewModel);
//让ViewModel拥有View的生命周期感应
getLifecycle().addObserver(viewModel);
//注入RxLifecycle生命周期
viewModel.injectLifecycleProvider(this);
}
//刷新布局
public void refreshLayout() {
if (viewModel != null) {
binding.setVariable(viewModelId, viewModel);
}
}
/**
* =====================================================================
**/
//注册ViewModel与View的契约UI回调事件
protected void registorUIChangeLiveDataCallBack() {
//加载对话框显示
viewModel.getUC().getShowDialogEvent().observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String title) {
showDialog(title);
}
});
//加载对话框消失
viewModel.getUC().getDismissDialogEvent().observe(this, new Observer<Void>() {
@Override
public void onChanged(@Nullable Void v) {
dismissDialog();
}
});
//跳入新页面
viewModel.getUC().getStartActivityEvent().observe(this, new Observer<Map<String, Object>>() {
@Override
public void onChanged(@Nullable Map<String, Object> params) {
Class<?> clz = (Class<?>) params.get(ParameterField.CLASS);
Bundle bundle = (Bundle) params.get(ParameterField.BUNDLE);
startActivity(clz, bundle);
}
});
//跳入ContainerActivity
viewModel.getUC().getStartContainerActivityEvent().observe(this, new Observer<Map<String, Object>>() {
@Override
public void onChanged(@Nullable Map<String, Object> params) {
String canonicalName = (String) params.get(ParameterField.CANONICAL_NAME);
Bundle bundle = (Bundle) params.get(ParameterField.BUNDLE);
startContainerActivity(canonicalName, bundle);
}
});
//关闭界面
viewModel.getUC().getFinishEvent().observe(this, new Observer<Void>() {
@Override
public void onChanged(@Nullable Void v) {
finish();
}
});
//关闭上一层
viewModel.getUC().getOnBackPressedEvent().observe(this, new Observer<Void>() {
@Override
public void onChanged(@Nullable Void v) {
onBackPressed();
}
});
}
public void showDialog(String title) {
if (dialog != null) {
dialog = dialog.getBuilder().title(title).build();
dialog.show();
} else {
MaterialDialog.Builder builder = MaterialDialogUtils.showIndeterminateProgressDialog(this, title, true);
dialog = builder.show();
}
}
public void dismissDialog() {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
}
/**
* 跳转页面
*
* @param clz 所跳转的目的Activity类
*/
public void startActivity(Class<?> clz) {
startActivity(new Intent(this, clz));
}
/**
* 跳转页面
*
* @param clz 所跳转的目的Activity类
* @param bundle 跳转所携带的信息
*/
public void startActivity(Class<?> clz, Bundle bundle) {
Intent intent = new Intent(this, clz);
if (bundle != null) {
intent.putExtras(bundle);
}
startActivity(intent);
}
/**
* 跳转容器页面
*
* @param canonicalName 规范名 : Fragment.class.getCanonicalName()
*/
public void startContainerActivity(String canonicalName) {
startContainerActivity(canonicalName, null);
}
/**
* 跳转容器页面
*
* @param canonicalName 规范名 : Fragment.class.getCanonicalName()
* @param bundle 跳转所携带的信息
*/
public void startContainerActivity(String canonicalName, Bundle bundle) {
Intent intent = new Intent(this, ContainerActivity.class);
intent.putExtra(ContainerActivity.FRAGMENT, canonicalName);
if (bundle != null) {
intent.putExtra(ContainerActivity.BUNDLE, bundle);
}
startActivity(intent);
}
/**
* =====================================================================
**/
@Override
public void initParam() {
}
/**
* 初始化根布局
*
* @return 布局layout的id
*/
public abstract int initContentView(Bundle savedInstanceState);
/**
* 初始化ViewModel的id
*
* @return BR的id
*/
public abstract int initVariableId();
/**
* 初始化ViewModel
*
* @return 继承BaseViewModel的ViewModel
*/
public VM initViewModel() {
return null;
}
@Override
public void initData() {
}
@Override
public void initViewObservable() {
}
/**
* 创建ViewModel
*
* @param cls
* @param <T>
* @return
*/
public <T extends ViewModel> T createViewModel(FragmentActivity activity, Class<T> cls) {
return ViewModelProviders.of(activity).get(cls);
}
}
首先BaseActivity中有两个抽象方法,这两个方法的具体用处不重要,只要知道这两个函数会被onCreate中的方法调用即可。这两个方法充当抽象方法必须由子类具体实现,子类是一定要实现的。
/**
* 初始化根布局
*
* @return 布局layout的id
*/
public abstract int initContentView(Bundle savedInstanceState);
/**
* 初始化ViewModel的id
*
* @return BR的id
*/
public abstract int initVariableId();
在这个BaseActivity中onCreate也就作为模板方法了,其他几个hook方法大家就自行寻找吧。
推广一下MMVMMHabit的项目地址:👕基于谷歌最新AAC架构,MVVM设计模式的一套快速开发库,整合Okhttp+RxJava+Retrofit+Glide等主流模块,满足日常开发需求。使用该框架可以快速开发一个高质量、易维护的Android应用。