答案:这是Test Case在JUnit框架1.0版本中的样子(为了简化,我们忽略了注释和很多方法):
public abstract class TestCase implements Test {
private String fName;
public TestCase(String name) {
fName= name;
}
public void run(TestResult result) {
result.startTest(this);
setUp();
try {
runTest();
}
catch (AssertionFailedError e) {
result.addFailure(this, e);
}
catch (Throwable e) {
result.addError(this, e);
}
tearDown();
result.endTest(this);
}
public TestResult run() {
TestResult result= defaultResult();
run(result);
return result;
}
protected void runTest() throws Throwable {
Method runMethod= null;
try {
runMethod= getClass().getMethod(fName, new Class[0]);
} catch (NoSuchMethodException e) {
e.fillInStackTrace();
throw e;
}
try {
runMethod.invoke(this, new Class[0]);
}
catch (InvocationTargetException e) {
e.fillInStackTrace();
throw e.getTargetException();
}
catch (IllegalAccessException e) {
e.fillInStackTrace();
throw e;
}
}
public int countTestCases() {
return 1;
}
}
新的需求要求允许测试重复进行、或在它们各自的线程中进行、或以上两者。
没有经验的程序员通常在遇到这样的新需求时进行子类型化。但是在这里,因为知道TestCase对象将需要能够在同一个线程中重复运行、或在各自独立的线程中重复运行,所以程序员知道:他们需要考虑得更多。
一种实现方法是:将所有的功能都添加给TestCase本身。许多开发者——尤其是那些不了解设计模式的开发者——将会这样做,而不考虑这会使他们的类变得臃肿。他们必须添加功能,所以他们将功能添加到任何可以添加的地方。下面的代码可能就是他们的实现:
public abstract class TestCase implements Test {
private String fName;
private int fRepeatTimes;
public TestCase(String name) {
this(name, 0);
}
public TestCase(String name, int repeatTimes) {
fName = name;
fRepeatTimes = repeatTimes;
}
public void run(TestResult result) {
for (int i=0; i < fRepeatTimes; i++) {
result.startTest(this);
setUp();
try {
runTest();
}
catch (AssertionFailedError e) {
result.addFailure(this, e);
}
catch (Throwable e) {
result.addError(this, e);
}
tearDown();
result.endTest(this);
}
}
public int countTestCases() {
return fRepeatTimes;
}
}
请注意run(TestResult result)方法变大了一些。他们还为TestCase类添加了另外的构造子。到目前为止,这还不算什么大事。并且,我们可以说:如果这就是所有必须做的事情,那么使用Decorator模式就是多余的。
现在,如果要让每个TestCase对象在其自己的线程中运行又怎样呢?这里也有一个可能的实现:
public abstract class TestCase implements Test {
private String fName;
private int fRepeatTimes;
private boolean fThreaded;
public TestCase(String name) {
this(name, 0, false);
}
public TestCase(String name, int repeatTimes) {
this(name, repeatTimes, false);
}
public TestCase(String name, int repeatTimes, boolean threaded) {
fName = name;
fRepeatTimes = repeatTimes;
fThreaded = threaded;
}
public void run(TestResult result) {
if (fThreaded) {
final TestResult finalResult= result;
final Test thisTest = this;
Thread t= new Thread() {
public void run() {
for (int i=0; i < fRepeatTimes; i++) {
finalResult.startTest(thisTest);
setUp();
try {
runTest();
}
catch (AssertionFailedError e) {
finalResult.addFailure(thisTest, e);
}
catch (Throwable e) {
finalResult.addError(thisTest, e);
}
tearDown();
finalResult.endTest(thisTest);
}
}
};
t.start();
result = finalResult;
} else {
for (int i=0; i < fRepeatTimes; i++) {
result.startTest(this);
setUp();
try {
runTest();
}
catch (AssertionFailedError e) {
result.addFailure(this, e);
}
catch (Throwable e) {
result.addError(this, e);
}
tearDown();
result.endTest(this);
}
}
}
public int countTestCases() {
return fRepeatTimes;
}
}
唔,这看起来开始变得更坏了。为了支持两个新的特征,我们现在拥有了三个构造子,而且run(TestResult result)方法的大小迅速的膨胀起来。
即使不管所有这些新代码,我们这些程序员还没有满足这些需求:我们仍然不能在各自的线程中重复运行测试。为了这个目的,我们必须添加更多的代码。算了,我就放过你吧。
重构可以帮助这些代码减小尺寸。但是只需要稍做思考:如果再接到一个新的需求,我们要怎么办?现在JUnit 3.1支持四种不同的TestCase修饰器,它们可以轻松的随意组合以获取所需的功能。同时,JUnit的实现仍然简单——没有混乱的代码。这种设计保持TestCase类的简单、轻量级,用户只需要在需要的时候对TestCase对象进行装饰即可,而且可以选择任何组合顺序。
很清楚,这是一个模式帮助简化设计的例子。这个例子也说明了缺乏经验的开发者怎样改善他们的设计——如果他们知道模式指出的重构目标。
使用模式来开发软件是聪明之举,但如果你缺乏使用模式的经验,它也可能是危险的。出于这个原因,我极力提倡模式学习组。这些学习组让人们在同伴的帮助下稳步前进而精通模式。
当人们了解模式并以受过训练的方式使用它们时,模式是最有用的——这种受过训练的方式就是XP的方式。以XP的方式使用模式鼓励开发者保持设计的简单、并完全根据需要对模式进行重构。它鼓励在设计早期使用关键的模式。它鼓励将问题与能帮助解决问题的模式相匹配。最后,它鼓励开发者编写模式的简单实现,然后根据需要发展它们。
在XP的场景中,模式的确更有用;而在包含对模式的使用时,XP开发则更有可能成功。
参考书目
[Beck1 00] Beck, Kent. Email on mailto:%20extremeprogramming@egroups.com, January 2000.
[Beck2 94] Patterns Generate Architectures, Kent Beck and Ralph Johnson, ECOOP 94
[GHJV1 95] Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides. 中译本:《设计模式:可复用面向对象软件的基础》,李英军等译。
[GHJV2 95] Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides. Pages 353-354 中译本:《设计模式:可复用面向对象软件的基础》,李英军等译,第6章。
[Jeffries 99] Jeffries, Ron. Patterns And Extreme Programming. Portland Pattern Repository. December, 1999
[Kerth 99] Kerth, Norm. Conversation, circa March, 1999.
[Kerievsky 96] Kerievsky, Joshua. Don’t Distinguish Between Classes And Inte易做图ces. Portland Pattern Repository. Circa 1996
上一个:使.NET应用程序开发标准化2(转)
下一个:创建一个n层的体系架构,它可以通过运用ADO.NET的功能使开发简单化。-zt(1)