20.先用它在来实现它

“请进,先完成所有的代码库,后面会有大量时间看到用户是否是如何思考,现在只有把代码仍过去就可以了,我保证它没有问题”

很多成功的公司都是靠着“吃自己的狗食”活着,也就是说,如果要让你的产品尽可能的好,自己先要积极地使用它幸运的是,我们不是在狗食业务,但是,我们的业务是要创造出能调用的API和可以使用的接口,事实上,在你刚做完设计但还没完成后面的实现的时候,应使用它,这个可行吗?

阅读 ‧ 电子书库

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

使用 被称为TDD(Test Driven Development,测试驱动开发的技术,你总是在一个失败的单元测试后才开始编码,测试)失败要么是因为测试方法不存在,要么是因为方法的逻辑不足以让编写能产生反馈的代码测试通过。

Writer test before writing code先测试,你就会站在代码用户的角度去思考,而不仅仅是一个单纯的实现者,这样就是很大区别,你就会发现以为自己要使用它们,所以能设计一个更有,更一致的接口除止之外,先写测试有助于消除过度复杂的设计,让你可以会考虑需要这些类,例如:TicTacToeBoard,Cell,Row,Colum,Player,User,Peg,Score和Rueles咱们从TicTacToeBoard类开始,它就代表了井字旗本身(从游戏的刻心逻辑而不是UI角度说)这可能是TicTacToeBoard类的第一测试,是用C#在NUtil测试框架下编写的, 它创造了一个游戏面板,用断言来检查游戏没有结束。

【TestFixture】

Public class TicTaToeTest

{

Private TicTaToeTest board;

[SetUp]

Public void createBord()

{

Board=new TicTacToeTest();

}

[Test]

Public void TestCreateBoard()

{

Assert.IsNotNull(board);

Assert.IsFalse(borad.Gameover);}}

测试失败,因为类TicTactoeBoard 还不存在,你会得到一个编译错误,如果它通过了,你一定很惊讶,不是吗?这也可能会发生,只是概率很小,但确实可能发生,在测试通过之前,先要确保测试是失败的,目的是希望暴露出测试中潜伏在的bug. 下面我们来实现这个类。

Public class TicTacToeBoard{

Public bool gameOver{

Get{

Return false;

}

}

}

在属性GameOver中,我们现在只返回false,一般情况下,你会用到必要色最少代码让测试通过,从某种角度上说,这就是在欺骗测试——你知道代码还没有完成。但是没有关系,后面的测试会迫使你在返回来的继承添加新的功能。

下一步是什么呢?首先,你必须决定谁先开始走第一步旗,我们就要设第一个比赛者。先为第一个比赛者先一个测试。

[Test]

Public void TestSetPirstPlayer(){//what should go here?

}

这时,测试会迫使你做一个决定,在完成它之前,你必须决定如何在代码中表示比赛者,如何把它们分配到面板上。这里有一个主意。

Board。SetFirstPlayer(new Player(”Mark”),”x”);这会告诉面板,游戏玩家Mark使用X。

这样当然可以。你真的需要Player这个类。或者第一个玩家的名字吗?也许,稍后你需要知道谁是赢家,但现在它还不是问题。YANG(你可以永远都不需要它)原则说过,如果不是真正需要他的时候,你就不应该实行这个功能。基于这一点,现在还没有足够的理由表示你需要的理由表示你需要Player这个类。

别忘了,我们还没有实行TicTacToeBoard类中的SetFirstPlayer()方法,并且还没有写play这个类,我们仍然是写一个测试,我们假设下面的代码是用来设置第一个玩家的Board。SetFirstPlayer(”x”);它表示设X为第一玩家,比第一版本需要简单,但是,这个版本隐藏着风险,你可以传任何字母给SetFirestPlayer()方法,这就是意味着你必须添加代码来检查参数是O还是X,并且需要知道如果它不是这两个值的时候该如何处理。因此要进一步简单化,我们有一个简单的标志来标明第一个玩家是O还是X,知道了这个,我们现在就可以写单元测试了[Test]Public void TestSetFirstPlay(){Board.FirstPlayPegIsx=true;Assert.IsTrue(board.FirstPlayerPegIsx);}我们可以将 FirstPlayerpegIsx 设为布尔型的属性,并把它设为期望的值。你看起来挺简单,也容易使用,比复杂的Player类容易很多,测试写好了。你就可以通过TicTacToeBoard类中实现FirestPlayerPegIsx属性,让测试通过。

你看,我们是以paayer类开始,最后却只使用了简单的布尔型的属性。这是如何做到的呢?这种简化就是编写代码之前让测试优先实现的。

但记住,我们不是要扔掉好的设计,就只用大量的布尔型来编码所有的东西。这里重点是:什么是成功的实现待定功能的最低成本,总之,程序员很容易走向另一个极端—— 一些不必要的过于复杂的事情——测试优先会帮助我们,防止我们走偏。

消除那些还没有编写的类,这会很容易的简化代码。相反,一旦你已经编写了代码,也许会强迫自己保留这些代码,并继续使用它|(即使代码已经过期作废很久了)

阅读 ‧ 电子书库

当你开发设计面向对象的系统的时候,可能会迫使自己使用对象,有一种倾向认为,面向对象的系统应该有对象的系统应该对象组成,我们迫使自己创建越来越多的对象类,不管他们是否真的需要,添加物用代码总是不好的想法TDD有机会让你编写的代码之前(或者至少深入到实现之前),可以深思熟虑将如何用它,这会迫使你去思考它的可用性和便利性,并然你的设计更加注重实效。

当然,设计不是开始编码的时候就结束了,你需要在它色生命周期中持续地添加测试,添加代码,并重新设计代码(更多信息参考第133也习惯28)先用它在来实现它。将TDD作为设计工具,它会为你带来更简单游实数的设计切身感受这种感受就是,只在有具体理由的时候才开始编码,你可以专注设计接口。而不会被很多实现的细节干扰平衡的艺术? 不要把测试优先和提交代码之前的测试等同起来,测试先行可以帮助你改进设计,但是你还是需要在提交代码之前做的测试。

? 任何一个设计都可以被改进

? 你在验证一个想法或者设计一个原型的时候,单元测试也许不适合,但是。万一这些代码不幸仓促演变成一个真正的系统,就必须要为他们添加测试(但是最后重新开始设计系统)? 单纯的单元测试无法保证好的设计,但它们会对设计有帮助,会然设计更加简单。