大话设计模式
字体: 16 + -

第157章

大b:“刚才说模板方法模式运用于一个业务对象。事实上,框架频繁使用模板方法模式,使得框架实现对关键逻辑的集中控制。”

大b:“我们需要为基本spring的应用做一个测试用例的基类。用于对类的方法进行单元测试。我们知道spring应用把需要用到的对象都定义在外部的xml文件中,也称为context。”

大b:“通常我们会把context分割成多个小的文件,以便于管理。在测试时我们需要读取context文件,但是并不是每次都读取所有的文件。读取这些文件是很费时间的。所以我们想把它缓存起来,只要这个文件被读取过一次,我们就把它们缓存起来。所以我们通过扩展junit的testcase类来完成一个测试基类。我们需要实现缓存的逻辑,其它开发人员只需要实现读取配置文件的方法即可。它不用管是否具有缓存。”

代码:

publicabstractcachecontexttestsextendstestcase{

privatestaticmapcontextmap=newhashmap();

protectedconfigurableapplicationcontextapplicationcontext;

protectedbooleanhascachedcontext(objectcontextkey){

returncontextkeytocontextmap.containskey(contextkey);

}

protectedconfigurableapplicationcontextgetcontext(objectkey){

stringkeystring=contextkeystring(key);

configurableapplicationcontextctx=(configurableapplicationcontext)contextkeytocontextmap.get(keystring);

if(ctx……null){

if(keyinstanceofstring[]){

ctx=loadcontextlocations((string[])key);

}

contextkeytocontextmap.put(keystring,ctx);

}

returnctx;

}

protectedstringcontextkeystring(objectcontextkey){

if(contextkeyinstanceofstring[]){

returnstringutils.arraytocommadelimitedstring((string[])contextkey);

}

else{

returncontextkey.tostring();

}

}

protectedconfigurableapplicationcontextloadcontextlocations(string[]locations){

returnnewclasspathxmlapplicationcontext(locations);

}

//覆写testcase的setup方法,在运行测试方法之前从缓存中读取context文件,如果缓存中不存在则初始化applicationcontext,并放入缓存。

protectedfinalvoidsetup()throwsexception{

string[]contextfiles=getconfiglocations();

applicationcontext=getcontext(contextfiles);

}

//读取context文件,由子类实现

protectedabstractstring[]getconfiglocations();

}

大b:“rendercode();这样子类只需要去实现getconfiglocations方法,提供需要读取的配置文件字符数组就可以了。至于怎么去读取context文件内容,怎么实现缓存,则无需关心。abstractcachecontexttests保证在运行所有测试之前去执行读取context文件的动作。注意这里setup方法被声明为protected,是因为setup方法是testcase类的方法。在这里setup方法被定义为final了,是确保子类不能去覆写这个方法,从而保证了父类控制的逻辑。”

小a:“如果使用过junit会发生什么问题?”

大b:“testcase的setup方法,就是在这个测试类的测试方法运行之前作一些初始化动作。如创建一些所有测试方法都要用到的公共对象等。在这里把setup方法声明为final之后,子类再也无法去扩展它了,子类同时还需要一些额外的初始化动作就无法实现了。可能你会说:‘把setup方法的final修饰符去掉就可以了隘。这样是可以的,但是去掉final修饰符后,子类是可以覆写setup方法,而去执行一些额外的初始化。而可怕的是,父类从此失去了必须读取context文件及缓存context内容的逻辑。为了解决这个问题,可以实现一个空方法onsetup。在setup方法中调用onsetup方法。这样子类就可以通过覆写onsetup方法来进行额外的初始化。”

//覆写testcase的setup方法,在运行测试方法之前从缓存中读取context文件,如果缓存中不存在则初始化applicationcontext,并放入缓存。

代码:

protected

nclass=“keyword”>finalvoidsetup()throwsexception{

string[]contextfiles=getconfiglocations();

applicationcontext=getcontext(contextfiles);

onsetup();

}

protectedvoidonsetup(){

}

//读取context文件,由子类实现

protectedabstractstring[]getconfiglocations();

}

rendercode();

小a:“为什么不把onsetup声明为abstract呢?”

大b:“这是因为子类不一定总是需要覆写onsetup方法。可以说onsetup方法是为了对setup方法的扩展。像onsetup这样的空方法就称之为勾子方法(hookmethod)。”