
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
昆明IT培训的老师今天给大家讲关于反射让模板方法模式更强大的知识点。
模板方法模式(Template Method Pattern)的定义是:定义一个操作中的算法骨架,将一些步骤延迟到子类中,使子类不改变一个算法的结构即可重定义该算法的某些特定步骤。简单的说,就是父类定义抽象模板作为骨架,其中包括基本方法(是由子类实现的方法,并且在模板方法中被调用)和模板方法(实现对基本方法的调度,完成固定的逻辑),它是用了简单的继承和覆写机制,昆明IT培训的老师来带大家看一个基本的例子。
昆明IT培训的老师经常会开发一些测试或演示程序,期望系统在启动时自动初始化,以方便测试或讲解,一般的做法是写一个SQL文件,在系统启动前自动导入,不过,这样不仅麻烦而且容易出错,于是我们就手写了一个自动初始化数据的框架:在系统(或容器)自动启动时自行初始化数据。但问题是每个应用程序要初始化的内容我们并不知道,只能由实现者自行编写,那我们就必须给作者预留接口,此时就得考虑使用模板方法模式了,代码如下:
1 public abstract class AbsPopulator {
2 //模板方法
3 public final void dataInitialing() throws Exception {
4 //调用基本方法
5 doInit();
6 }
7
8 //基本方法
9 protected abstract void doInit();
10 }
这里定义了一个抽象模板类AbsPopulator,它负责数据初始化,但是具体要初始化哪些数据则是由doInit方法决定的,这是一个抽象方法,子类必须实现,我们来看一个用户表数据的加载:
public class UserPopulator extends AbsPopulator{
@Override
protected void doInit() {
//初始化用户表,如创建、加载数据等
}
}
该系统在启动时查找所有的AbsPopulator实现类,然后dataInitialing实现数据的初始化。那大家可能要想了,怎么让容器指导这个AbsPopulator类呢?很简单,如果是使用Spring作为Ioc容器的项目,直接在dataInitialing方法上加上@PostConstruct注解,Spring容器启动完毕后自动运行dataInitialing方法。具体大家看spring的相关知识,这里不再赘述。
现在问题是:初始化一张User表需要非常多的操作,比如先建表,然后筛选数据,之后插入,最后校验,如果把这些都放入到一个doInit方法里会非常庞大(即使提炼出多个方法承担不同的责任,代码的可读性依然很差),那该如何做呢?又或者doInit是没有任何的也无意义的,是否可以起一个优雅而又动听的名字呢?
答案是我们可以使用反射增强模板方法模式,使模板方法实现对一批固定的规则的基本方法的调用。代码是最好的交流语言,我们看看怎么改造AbsPopulator类,代码如下:
public abstract class AbsPopulator {
//模板方法
public final void dataInitialing() throws Exception {
//获得所有的public方法
Method[] methods = getClass().getMethods();
for (Method m : methods) {
//判断是否是数据初始化方法
if (isInitDataMethod(m)) {
m.invoke(this);
}
}
}
//判断是否是数据初始化方法,基本方法鉴定器
private boolean isInitDataMethod(Method m) {
return m.getName().startsWith("init")// init开始
&& Modifier.isPublic(m.getModifiers())//公开方法
&& m.getReturnType().equals(Void.TYPE)//返回值是void
&& !m.isVarArgs()//输出参数为空
&& !Modifier.isAbstract(m.getModifiers());//不能是抽象方法
}
}
在一般的模板方法模式中,抽象模板(这里是AbsPopulator类)需要定义一系列的基本方法,一般都是protected访问级别的,并且是抽象方法,这标志着子类必须实现这些基本方法,这对子类来说既是一个约束也是一个负担。但是使用了反射后,不需要定义任何抽象方法,只需要定义一个基本方法鉴定器(例子中的isInitDataMethod)即可加载符合规则的基本方法。鉴别器在此处的作用是鉴别子类方法中哪些是基本方法,模板方法(例子中的dataInitaling)则需要基本方法鉴定器返回的结果通过反射执行相应的方法。
此时,如果需要进行大量的初始化工作,子类的实现就非常简单了,代码如下:
public class UserPopulator extends AbsPopulator {
public void initUser() {
/*初始化用户表,如创建、加载数据等*/
}
public void initPassword() {
/*初始化密码*/
}
public void initJobs() {
/*初始化工作任务*/
}
}
UserPopulator类中的方法只要符合基本方法鉴别器条件即会被模板方法调用,方法的数据量也不再受父类的约束,实现了子类灵活定义基本方法、父类批量调用的功能,并且缩减了子类的代码量。
如果大家熟悉JUnit的话,就会看出此处的实现与JUnit非常相似,JUnit4之前要求测试的方法名必须是以test开头的,并且无返回值、无参数,而且是public修饰,其实现的原理与此非常类似,昆明IT培训的老师提醒大家去看看Junit的源码。