2005年四月月 发布的文章

一个Xml Parser的TDD开发过程-Tony与Alex的对话系列

Tony : Alex今天我们来做一个xml parser.我们使用的开发工具为Eclipse + JUnit
Alex : 好啊,喜欢接受挑战。
Tony : 先看看我们要解析的xml file的样子:

 
  
   
   
    
 

使用XmlSpy自动生成其DTD如下:

<!ATTLIST test 
 name CDATA #REQUIRED
>

<!ATTLIST suite 
 name CDATA #REQUIRED
 category CDATA #REQUIRED
>

<!ATTLIST classes 
 name CDATA #REQUIRED
>

我们就是要将该xml文件中的test , suite, class标签中存储的信息读出来,存储在数据结构中XmlTest中。所以我们的Parser的parse方法返回的就是个XmlTest的引用。我们通过上面的xml文件,我们可以确定一个类-XmlTest,带有一个成员name,创建XmlTest, 这个so easy

public class XmlTest {
    private String name;
   
    /**
     * @param name The name to set.
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return Returns the name.
     */
    public String getName() {
        return name;
    }
   
}

接下来,tdd的经典流程就是先写出feature list,来我们琢磨一下吧,

Alex :首先我们要知道我们要解析的xml文件的存放处,如果用户提供一个错误的位置,我们应该抛出异常。—-(1)

Tony :Great idea! 那我们就动手把。首先我们创建一个测试类ParserTest,添加一个方法

testNotFoundXmlFile,并创建一个Parser实例,将错误的xml文件路径传给它。代码如下:
public class ParserTest extends TestCase {
    public void testNotFindXmlFile(){
        try{
            Parser parser = new Parser("D:\\TestDemo\\demo1.xml");
            XmlTest result = parser.parse();
            fail("The FileNotFileFoundException should be raised!");
        }catch(FileNotFoundException fnfe){
           
        }
    }
}

run the test –> red bar
由于Parser类暂不存在,当然测试通过不了了。添加Parser类,并添加parse方法
public class Parser {
    private String xmlFilePath;
    public Parser(String xmlFilePath){
        this.xmlFilePath = xmlFilePath;
    }
   
    public XmlTest parse() {
        return null;
    }
}

run the test–>red bar

Alex :我来修改一下parse方法
public XmlTest parse()throws FileNotFoundException{
        File file = new File(xmlFilePath);
        if(!file.exists()){
            throw new FileNotFoundException();
        }
        return null;
    }
再试试吧。

Tony : Ok,green bar ,so beautiful!!! 我们这么快就完成了第一个feature了。
Alex : 是呀,不过这只是个开头。最主要的功能还没有实现呢。Just go on!

Tony : ok ,继续。

Alex :从dtd可以看出,该xml的根元素是test标签。下一步我们要完成的就是读取根元素test标签的属性信息—-(2)

Tony :i could not agree with you any more !! 这样就涉及到一些xml解析的知识了,我们来学习一下吧。

a short break !
Tony and Alex are studying JAXP together!

Tony :怎么样,ok了么
Alex :差不多了,JAXP不难,我们继续把。
Tony :是呀,按照JAXP给的例子,我们可能很容易就达到目的。ok,让我们add a new test.

public void testReadTestNode(){
        try{
            Parser parser = new Parser("D:\\TestDemo\\demo.xml");
            XmlTest result = parser.parse();         
            assertEquals("my first test", result.getName());
        }catch(FileNotFoundException fnfe){
            fail("The program should not reach here");
        }
}

run the test, as we expect it is a red bar.

Alex :根据JAXP的例子,我们解析特殊的xml file需要自己定制一个DefaultHandler的子类。然后将之传给SAXParser的parse方法。

Tony :好吧,让我们创建一个MyHandler class吧!

Alex :根据SAX的解析流程,我们要override some methods

Tony :要让我们的测试用例通过,我们得override DefaultHandler的startElement method,通过MyHandler获取我们所要的内容。

public class MyHandler extends DefaultHandler {

    private XmlTest curXmlTest = null;
   
    public XmlTest getXmlTest(){
        return curXmlTest;
    }
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        if ("test".equals(qName)) {
            curXmlTest = new XmlTest();
            curXmlTest.setName(attributes.getValue("name"));
        }
    }   
}

Alex :同时我们还要修改一下我们的Parser类的parse方法,如下:
parser.parse(file, myHandler);
result = myHandler.getXmlTest();
这样我们通过MyHandler取得了我们需要的东西,相信这一回test一定会通过的

Tony : 如你所愿,通过了。

Alex : 看看我们的代码,有些乱是吧。我们来重构一下吧。

Tony :我们先来重构一下测试代码。利用Junit提供的setUp将testfixture部分封装起来。

Alex :我来做。

public class ParserTest extends TestCase {

    private Parser parser;
   
    protected void setUp() throws Exception {       
        super.setUp();
        parser = new Parser("D:\\TestDemo\\demo.xml");
    }
   
    //…

   public void testReadTestNode(){
        try{           
            XmlTest result = parser.parse();         
            assertEquals("my first test", result.getName());
        }catch(FileNotFoundException fnfe){
            fail("The program should not reach here");
        }
    }
}
运行一下测试,一切Ok.还是那条令人兴奋的green bar

Tony : 以应用代码作为基础的的测试代码重构做完了,这两部分互相保证还是让我们很是放心的。

Alex :是呀,要不继续。我们来想想下一个todo item。你觉得我们是不是该读取suite标签信息了亚?———-(3)

Tony :就按你所说我们继续添加测试用例testReadSuiteNode, 我们修改一下XmlTest使之存储Suite信息。

Alex :那我们再创建一个类XmlSuite,然后在XmlTest中包含XmlSuite的集合。

Tony : 那我们的testReadSuiteNode可以这样写
public void testReadSuiteNode() {
        try {
            XmlTest result = parser.parse();
            assertEquals(1, result.getXmlSuites().size());
            assertEquals("test1", result.getXmlSuites().get(0).getName());
            assertEquals("unit", result.getXmlSuites().get(0).getCategory());
        } catch (FileNotFoundException fnfe) {
            fail("The program should not reach here");
        }
}

Alex : 我们需要添加XmlSuite类,还要改写XmlTest

Tony :我们在XmlTest中用一个ArrayList来存储XmlSuite。并提供get和add suite方法

Alex :我完成了,我们运行一下吧。green bar ,ok, let us have a short break!

看完“程序员”2005-04期一些想法

每次看完《程序员》杂志都会有些新的收获,这次看的是《程序员》2005年第4期,顺便把一些阅读过程中产生的想法记录了下来。

[软件建模,大势所趋]
看完微软、IBM、Borland等公司的最新动态,感觉软件建模是大势所趋,以前虽然也有众多建模工具,但是由工具支持得不好,建模的各个阶段彼此脱节,使用户体验(user experience)很差。随着Microsoft VSTS(Visual Studio Team System)的发布在即、随着Borland的ALM(Application Lifecycle Management )工具及基于Eclipse的产品计划的实施,相信在不久的将来软件建模会有一个很大的改观。

[源代码就是设计]
Artima上出现了“源代码就是设计”的续篇,作者阐明其观点:
a)“the source code is the design” does not mean “don't do design , just code”;
b) the design is a process but not a product;Somethings like UML diagrams or CRC cards are not the real software design.
c)“we need good architectures (top level design), good abstractions (class design), and good implementations (low level design). ” UML diagrams or CRC cards可以帮我们完成top level design和class design,但是这并不意味着设计结束了。我们还需要low level design — that is the source code。
d)“The only way we validate a software design is by building it and testing it. There is no silver bullet, and no "right way" to do design”作者坚持认为“不到写完代码,代码通过测试,设计工作就不算完。

编码可以看作low level design,它是设计工作的延续,不到编码完成测试结束,我们永远不能知道我们的设计是否正确。

[关注开源,参与开源]
相信现在国内外任何一家软件厂商都不能不考虑“开源的影响力”了。从JDO2.0的起死回生,到EJB 3.0采纳了众多开源组织的建议,我们清醒地看到了这一点。同时我们国内的开源又是怎样的呢?不可否认的是中国的开源发展也是迅速的,但是还远远没达到“普及”的程度。我相信现在国内很多软件公司对开源的理解也只停留在“免费使用”这个层面上,用的时候还要“挑三捡四”一番。这和国外的“开源洪流”形成了鲜明的对比。最近传出Borland公司即将发布基于Eclipse的产品,BEA加入开源世界,虽说这背后有其“利益”因素在驱使,但是这也为我们国内厂商指明了一个大方向“关注开源,参与开源”。开源需要激情,国人期待在中国出现大师级的开源领袖,而实现这一目标首先需要你参与到开源世界中去。希望国内的软件厂商能把眼光放的更远一些,而不仅仅是现在的免费“索取”。

[技术与市场]
这是一个很有意思而又值得大家反思的话题,超前技术到底能不能带来市场收益上的“超前”,当年的Apple公司的Macintosh和鼠标的发明给了我们些许的启示“技术和市场不能脱节”,值得我们反思。

[文档的作用]
文档多被作为一种“成果物”,而不是沟通和交流的手段。大多数人为了文档而文档。文档形成后,便少有人问津了。

[AOP是最佳解决方案]
在最近的通用框架项目中设计“网管”模块,几乎框架中的所有模块都要使用其中的告警机制,感觉如果用AOP实现肯定会特别舒服,不过我们使用的是C语言开发,虽说C语言是“万能的”,不过到现在为止,我还没见到用C实现的AOP框架。

[要事第一]
这几天很多事情都摆在面前,干这件事的时候还在想着另外一件事,导致哪件事都没能做好。突然想起“高效能人士的7个习惯”一书中的一个习惯就是“要事第一”,把自己的事情按优先级分类,确定deadline,这回做起来就顺手一些了。

[习惯的定义]
昨晚,刚刚接受过“高效能人士的7各习惯”培训的my roommate问我一个问题,“什么是习惯”?愕然间,听到他的解析“习惯是知识、技巧与意愿三者的混合体,我们有付诸行动的愿望,在这样的一个思维惯性的推动下,久而久之就形成了经常性的行为–习惯”。Note:习惯是各中性词,怀习惯同样符合这一定义。

[身体才是革命的本钱]
昨天下午突然感觉身体有恙,浑身冒冷汗,做什么都做不进去,离开座位walk around for several minutes,还是感觉极差,所以请了假,早早回了寝室,躺在床上突然想到一句长辈们总在我们耳畔重复的话“身体是革命的本钱”。在寝室看完了上个星期买的《程序员》2005年第4期,写下了以上的文字。

My first knowledge share

第一次和大家分享知识的时间可以说比我预计的要“晚”,也可以说比我预计的要“早”。解释一下,之所以说“晚”,是因为我自己曾经准备了多个“topic”,但是总是感觉时机不成熟儿没能成行;之所以说“早”,是因为我决心要share的那个topic准备的还不够成熟。

这次share被定在五一前,由于我觉得那个topic准备的还不够成熟,或者说是我能让我满意。也许可能还是我那个追求完美的心理在作祟。有一句谚语叫“Practice makes perfect”。达到令我满意的境地需要一个过程,但是如果没有第一次,你永远也达不到perfect的境地。所以我给我自己的确定了一些基本的准则:

– 不要期望过高,演砸了也没关系,心态平和;
– 把这次share当成一次发现自身问题的机会;
– 精心准备(experience from dreamhead)。

希望这次share对我来说是一个新的起点。




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:


以太币:


如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn


文章

评论

  • 正在加载...

分类

标签

归档











更多