RSS 2.0 Feed
TDD/DDD/BDD
摘要:【原文地址】 http://codingdojo.org/cgi-bin/wiki.pl?KataPotter 【题目描述】 从前有一套5册的系列书,是关于一个名叫“Harry”的英国英雄的(起码在该问题出现时还只有5本,但之后又出了无数本)。全世界的儿童都认为他非常了不起,自然,出版商也这么认为。作为一种对全人类的无比慷慨的姿态,(以及为了提高销量),出版商制定了下述标价模型,来充分利用Harry的魔力: 5册书的任何1本的价格为8欧元。但,假如你从该系列购买2本不同的书的话,这2本书你可以得到5%的折扣。如果你购买3本不同的书,你可以得到10%的折扣。如果你购买4本不同的书,你可以得到20%的折扣。如果你购买整个系列,你可以得到25%的折扣。 注意,如果你购买4本书,其中3本书是不同的,那么对那3本书,你可以得到10%的折扣,但第4本的价格仍然是原价-8欧元。 Potter狂热席卷全国,各地少年儿童的父母装着满车的Potter书在柜台外排起了长队。。。你的任务是写段代码,计算每个购物车的金额,尽可能给与最大折扣。 譬如,这个购物车里的图书算多少钱? 第一册书有2本第二册书有2本第三册书有2本第四册书有1本第五册书有1本 (答案是 51.20 欧元)。 【提示】 一开始你也许会发现这个题目很容易,你可以从0本书,1本书,2本同样的书,2本不同的书。。。。等等购物车例子开始,迈小步子前进,逐步引进复杂性。 然后,当你坐下来,计算上面的购物车例子该算多少钱时,你会发现其中的曲折。金额不是5*8*0.75+3*8*0.90,实际上是4*8*0.8+4*8*0.8。这个题目的验收测试并没有错误,诀窍是你该如何编程来决定2份4册的书比1份3册的书和1份5册的书便宜。 你也许需要引进一些优化算法,但不用太费劲,按部就班地解决问题,相信自己在新的需求来临时,能进行概括,改进方案。 【推荐的测试例子】(是以Ruby语言写的,但应该很容易转换成C#代码的):def testBasics assert_equal(0, price([])) assert_equal(8, price([0])) assert_equal(8, price([1])) assert_equal(8, price([2])) assert_equal(8, price([3])) assert_equal(8, price([4])) assert_equal(8 * 2, price([0, 0])) assert_equal(8 * 3, price([1, 1, 1])) end def testSimpleDiscounts assert_equal(8 * 2 * 0.95, price([0, 1])) assert_equal(8 * 3 * 0.9, price([0, 2, 4])) assert_equal(8 * 4 * 0.8, price([0, 1, 2, 4])) assert_equal(8 * 5 * 0.75, price([0, 1, 2, 3, 4])) end def testSeveralDiscounts assert_equal(8 + (8 * 2 * 0.95), price([0, 0, 1])) assert_equal(2 * (8 * 2 * 0.95), price([0, 0, 1, 1])) ......[阅读全文]

posted @ | Feedback (2) |

摘要:首先把需求写下来: 1.【需求(因为原题的需求有点含糊,我在这里做了些简化)】反转一个句子:把一个句子里的以空格为间隔的英文单词,反向排列后以单个空格将其隔开后输出。(标点符号什么的先不考虑,就假设这些东西已经被另外的对象处理过了) 有人大概会说,有必要一本正经地把这么小的需求写下来么,有这功夫,他都能把整个程序写完了,而且这个程序满足所有的需求,包括各种边界条件等。对此,我只能说,你真棒! 对这样简单的东西大张旗鼓的,有些好笑。但我一般还是喜欢把需求按照自己的理解,自己的文字写下来,与客户(在这里,部分是gigix-因为需求是从他的测试方法里推出来的,部分是我-因为里面的有些东西需要最后认定)确认为好。 接下来,从哪里开始呢? 我们要开发什么?反转一个句子。我们这里准备使用.NET,需要把相应的功能放在一个类里,这个类取什么名字好?gigix用的是StringReverser作为类名,这个名字好像有点歧义,reverse string(反转字符串)?其涵义应该跟我们要满足的需求有点不同,我们需要的是reverse the order of the words in a sentence(把句子里的单词的次序倒转),但好像把这些词合起来变成类名有点过分,我们就简单一点,叫WordOrderReverser吧。 我们要写维护性好的程序,程序必须有很好的可读性(codebetter.com的Scott Bellware称之为Soluble Code ),对类,成员,方法的命名很重要,应该反映出它们所代表的东西。Pete Goodliffe在他的《Code Craft: The Practice of Writing Excellent Code》的第三章《What’s in a Name?》里详细讨论了取个有意义的名字的重要性。 因为我们要做TDD,先从测试类开始 (如果你要跟着做的话,在Visual Studio 2005里, 建立一个C#的类库方案,取名为JoyCodeTDDDemo,再往方案里加一个类库项目,取名为JoyCodeTDDDemoTest。在JoyCodeTDDDemoTest中,删除对System.Data和System.Xml的引用,删除系统自动生成的Class1.cs,添加对nunit.framework最新版本的程序集引用,以及对JoyCodeTDDDemo的项目引用。另外,不知道你用什么东西运行测试,我用的是TestDriven.NET的VS Add-in)。 在测试项目JoyCodeTDDDemoTest里,添加一个新的类文件,给新加的类文件命名为WordOrderReverserTest.cs,添加对NUnit.Framework的using,将其中的类设成public,并添加[TestFixture]: using System;using NUnit.Framework; namespace JoyCodeTDDDemoTest{[TestFixture]public class WordOrderReverserTest{ }} 编译一下,确认没问题。 让我们来写第一个测试方法,就象“抛出异常的爱”的做法一样,测试只有一个单词的情形: [Test]public void TestOneWord(){String str = "abc";WordOrderReverser reverser = new WordOrderReverser();Assert.AreEqual("abc", reverser.Reverse(str));} 我们在上面决定把包含应用代码的类名取为WordOrderReverser,在这里我们预期它有一个叫Reverse的方法,实现我们用户的需求。在这里我们选择这个方法接受一个参数,但我们也可以把需要反转的字符串传入WordOrderReverser的构造器,譬如这样 String str = "abc";WordOrderReverser reverser = new WordOrderReverser(str);Assert.AreEqual("abc", reveser.Reverse()); 哪个比较好?后面这个做法决定了我们的类只反转传入的字符串,不能反转其他的字符串。这样的做法也许在很多情形下比较合适,但我们这里是把某个算法(如果反转句子里的词也算什么算法的话)封装在这个类里,在实际情形中,也许需要同时反转多个句子,所以也许采用第一种做法比较好,因为第二种做法需要为每个句子生成一个相应的对象才能做反转操作。。。。【打住,打住,这是在做upfront design了,需求里有这么说么?没有。】,就按直觉吧。 很明显,现在无法编译。在JoyCodeTDDDemo项目里,删除系统自动生成的Class1.cs,添加一个名为WordOrderReverser.cs的文件,再加一个Reverse方法: using System; namespace JoyCodeTDDDemo{public class WordOrderReverser{public string Reverse(String str){return null;}}} 别忘了在WordOrderReverserTest.cs里加一个引用, using JoyCodeTDDDemo; 编译通过,运行测试失败,出现红灯: TestCase 'JoyCodeTDDDemoTest.WordOrderReverserTest.TestOneWord' failed: Expected: "abc"But was: nullD:\labs\JoyCodeTDDDemo\JoyCodeTDDDemoTest\WordOrderReverserTest.cs(15,0): at JoyCodeTDDDemoTest.WordOrderReverserTest.TestOneWord() TestFixture failed: Child test......[阅读全文]

posted @ | Feedback (4) |