反射让模板方法模式更强大
2015-11-06 17:13
190 查看
模板方法模式(Template Method Pattern)的定义是:定义一个操作中的算法骨架,将 一些步骤延迟到子类中,使子类不改变一个算法的结构即可重定义该算法的某些特定步骤。简单地说,就是父类定义抽象模板作为骨架,其中包括基本方法(是由子类实现的方法,并 且在模板方法被调用)和模板方法(实现对基本方法的调度,完成固定的逻辑),它使用了 简单的继承和覆写机制,我们来看一个基本的例子。
我们经常会开发一些测试或演示程序,期望系统在启动时自行初始化,以方便测试或讲解,一般的做法是写一个SQL文件,在系统启动前手动导入,不过,这样不仅麻烦而且还容 易出现错误,于是我们就自己动手写了一个初始化数据的框架:在系统(或容器)启动时自行初始化数据。但问题是毎个应用程序要初始化的内容我们并不知道,只能由实现者自行编写,那我们就必须给作者预留接口,此时就得考虑使用模板方法模式了,代码如下:
这里定义了一个抽象模板类AbsPopulator,它负责数据初始化,但是具体要初始化哪些 数据则是由dolnit方法决定的,这是一个抽象方法,子类必须实现,我们来看一个用户表数据的加载:
public abstract class AbsPopulator { //模板方法 public final void datalnitialing () throws Exception { //调用基本方法 dolnit(); } //基本方法 protected abstract voiddolnit(); }
该系统在启动时,査找所有的AbsPopulator实现类,然后datalnitialing实现数据的初始 化。那读者可能要想了,怎么让容器知道这个AbsPopulator类呢?很简单,如果是使用Spring 作为IoC容器的项目,直接在datalnitialing方法上加上@P〇stConstruct注解,Spring容器启动 完毕后会自动运行datalnitialing方法,由于这里的原理超出了本书的范畴,不再赘述。
现在的问题是初始化一张User表需要非常多的操作,比如先建表,然后筛选数据, 之后插入,最后校验,如果把这些都放到一个dolnit方法里会非常庞大(即使提炼出多个方 法承担不同的职责,代码的可读性依然很差),那该如何做呢?又或者dolnit是没有任何的 业务意义的,是否可以起一个优雅而又动听的名字呢?
答案是我们可以使用反射增强模板方法模式,使模板方法实现对一批固定规则的基本方法的调用。代码是最好的交流语言,我们看看怎么改造AbsPopulator类,代码如下:
在一般的模板方法模式中,抽象模板(这里是AbsPopulator类)需要定义一系列的基 本方法,一般都是protected访问级别的,并且是抽象方法,这标志着子类必须实现这些基 本方法,这对子类来说既是一个约束也是一个负担。但是使用了反射后,不需要定义任何抽象方法,只需定义一个基本方法鉴别器(例子中isInitDataMethod)即可加载符合规则的 基本方法。鉴别器在此处的作用是鉴别子类方法中哪些是基本方法,模板方法(例子中的
datalnitialing)则根据基本方法鉴别器返回的结果通过反射执行相应的方法。
此时,如果需要进行大量的数据初始化工作,子类的实现就非常简单了,代码如下:
UUserPopulator类中的方法只要符合基本方法鉴别器条件即会被模板方法调用,方法的数 据量也不再受父类的约束,实现了子类灵活定义基本方法、父类批量调用的功能,并且缩减了子类的代码量。
如果读者熟悉JUnit的话,就会看出此处的实现与JUnit非常类似,JUnit4之前要求测 试的方法名必须是以test开头的,并且无返回值、无参数,而且有public修饰,其实现的原 理与此非常相似,读者有兴趣可以看看JUnit的源代码。
注意决定使用模板方法模式时,请尝试使用反射方式实现,它会让你的程序更灵活、更强大。
我们经常会开发一些测试或演示程序,期望系统在启动时自行初始化,以方便测试或讲解,一般的做法是写一个SQL文件,在系统启动前手动导入,不过,这样不仅麻烦而且还容 易出现错误,于是我们就自己动手写了一个初始化数据的框架:在系统(或容器)启动时自行初始化数据。但问题是毎个应用程序要初始化的内容我们并不知道,只能由实现者自行编写,那我们就必须给作者预留接口,此时就得考虑使用模板方法模式了,代码如下:
public abstract class AbsPopulator { //模板方法 public final void datalnitialing () throws Exception { //调用基本方法 dolnit(); } //基本方法 protected abstract voiddolnit(); }
这里定义了一个抽象模板类AbsPopulator,它负责数据初始化,但是具体要初始化哪些 数据则是由dolnit方法决定的,这是一个抽象方法,子类必须实现,我们来看一个用户表数据的加载:
public abstract class AbsPopulator { //模板方法 public final void datalnitialing () throws Exception { //调用基本方法 dolnit(); } //基本方法 protected abstract voiddolnit(); }
该系统在启动时,査找所有的AbsPopulator实现类,然后datalnitialing实现数据的初始 化。那读者可能要想了,怎么让容器知道这个AbsPopulator类呢?很简单,如果是使用Spring 作为IoC容器的项目,直接在datalnitialing方法上加上@P〇stConstruct注解,Spring容器启动 完毕后会自动运行datalnitialing方法,由于这里的原理超出了本书的范畴,不再赘述。
现在的问题是初始化一张User表需要非常多的操作,比如先建表,然后筛选数据, 之后插入,最后校验,如果把这些都放到一个dolnit方法里会非常庞大(即使提炼出多个方 法承担不同的职责,代码的可读性依然很差),那该如何做呢?又或者dolnit是没有任何的 业务意义的,是否可以起一个优雅而又动听的名字呢?
答案是我们可以使用反射增强模板方法模式,使模板方法实现对一批固定规则的基本方法的调用。代码是最好的交流语言,我们看看怎么改造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) { // TODO Auto-generated method stub return m.getName().startsWith("init") && // init 开始 m.getReturnType().equals(Void.TYPE) && // 返回值是 void Modifier.isPublic(m.getModifiers()) &&// 公开方法 !m.isVarArgs() &&//输入参数为空 !Modifier.isAbstract(m.getModifiers());// 不能是抽象方法 } }
在一般的模板方法模式中,抽象模板(这里是AbsPopulator类)需要定义一系列的基 本方法,一般都是protected访问级别的,并且是抽象方法,这标志着子类必须实现这些基 本方法,这对子类来说既是一个约束也是一个负担。但是使用了反射后,不需要定义任何抽象方法,只需定义一个基本方法鉴别器(例子中isInitDataMethod)即可加载符合规则的 基本方法。鉴别器在此处的作用是鉴别子类方法中哪些是基本方法,模板方法(例子中的
datalnitialing)则根据基本方法鉴别器返回的结果通过反射执行相应的方法。
此时,如果需要进行大量的数据初始化工作,子类的实现就非常简单了,代码如下:
public class UserPopulator extends AbsPopulator { public void initUser(){ System.out.println("initUser"); } public void initPassWord(){ System.out.println("initPassWord"); } public void initJobs(){ System.out.println("initJobs"); } }
UUserPopulator类中的方法只要符合基本方法鉴别器条件即会被模板方法调用,方法的数 据量也不再受父类的约束,实现了子类灵活定义基本方法、父类批量调用的功能,并且缩减了子类的代码量。
如果读者熟悉JUnit的话,就会看出此处的实现与JUnit非常类似,JUnit4之前要求测 试的方法名必须是以test开头的,并且无返回值、无参数,而且有public修饰,其实现的原 理与此非常相似,读者有兴趣可以看看JUnit的源代码。
注意决定使用模板方法模式时,请尝试使用反射方式实现,它会让你的程序更灵活、更强大。
相关文章推荐
- Find the maximum subarray XOR in a given array
- Android NDK 1 - 指令集、CPU架构与汇编
- worklight获取设备的deviceID
- excel文件排版为印刷版:对文件进行规范化,批量删除空行
- Android PopWindow的简单应用
- adb shell下提示read-only file system
- apache+php安装 配置
- 安卓app之间socket通信,支持多线程
- C语言:浮点数在内存中的表示
- libevent代码阅读(10)——“hello-world.c”之 把事件处理器添加到event_base对象的注册的事件处理器的队列
- PHP识别二维码(php-zbarcode)
- selenium webdriver 执行javascript代码
- crontab定时备份mysql不执行
- PreparedStatement的setString导致越界问题解决方案
- 瀑布流
- 802.11i协议
- Android中同步类Mutex(AutoMutex)与Condition。
- 高德地图 如何查询该坐标是否在 我画的范围内 用到了”射线法“
- 企业级架构几个关键点
- Xcode7.0beta真机调试出现"Could not find Developer Disk Image"揭示