<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://weblogs.asp.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Chris McKenzie&amp;#39;s Blog : Test Driven Development (TDD)</title><link>http://weblogs.asp.net/taganov/archive/tags/Test+Driven+Development+_2800_TDD_2900_/default.aspx</link><description>Tags: Test Driven Development (TDD)</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP1 (Build: 20510.895)</generator><item><title>Here's a question</title><link>http://weblogs.asp.net/taganov/archive/2006/04/17/443128.aspx</link><pubDate>Mon, 17 Apr 2006 21:09:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:443128</guid><dc:creator>taganov</dc:creator><author>taganov</author><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/taganov/rsscomments.aspx?PostID=443128</wfw:commentRss><comments>http://weblogs.asp.net/taganov/archive/2006/04/17/443128.aspx#comments</comments><description>&lt;p&gt;I've been practicting Test Driven Development pretty religiously for about a year now. That and the reading I've done on design patterns and software architecture have dramatically improved my software design and quality. I've got an automated build server set up as well.&lt;/p&gt; &lt;p&gt;That said, I need to print a Reporting Services report directly to the printer. I have code that works, but I haven't been able to figure out how to write a unit test for this that can be executed by the build server. I don't want the build server to send the report to an in-house printer everytime it runs. I'd much rather print it to a file, but a file printer asks for user input (the file name).&lt;/p&gt; &lt;p&gt;Any ideas?&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=443128" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/taganov/archive/tags/Visual+Studio+Tools+for+Office+_2800_VSTO_2900_/default.aspx">Visual Studio Tools for Office (VSTO)</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Test+Driven+Development+_2800_TDD_2900_/default.aspx">Test Driven Development (TDD)</category></item><item><title>Humor for Agile Developers</title><link>http://weblogs.asp.net/taganov/archive/2006/02/06/437543.aspx</link><pubDate>Mon, 06 Feb 2006 19:19:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:437543</guid><dc:creator>taganov</dc:creator><author>taganov</author><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/taganov/rsscomments.aspx?PostID=437543</wfw:commentRss><comments>http://weblogs.asp.net/taganov/archive/2006/02/06/437543.aspx#comments</comments><description>&lt;p&gt;Especially funny to me is wordUnit!&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.waterfall2006.com/"&gt;http://www.waterfall2006.com/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=437543" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/taganov/archive/tags/Miscellaneous/default.aspx">Miscellaneous</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Design+Patterns/default.aspx">Design Patterns</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Test+Driven+Development+_2800_TDD_2900_/default.aspx">Test Driven Development (TDD)</category></item><item><title>On explaining the importance of a Data Access Layer</title><link>http://weblogs.asp.net/taganov/archive/2005/04/12/399999.aspx</link><pubDate>Tue, 12 Apr 2005 14:54:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:399999</guid><dc:creator>taganov</dc:creator><author>taganov</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/taganov/rsscomments.aspx?PostID=399999</wfw:commentRss><comments>http://weblogs.asp.net/taganov/archive/2005/04/12/399999.aspx#comments</comments><description>&lt;p&gt;Nice &lt;a href="http://weblogs.sqlteam.com/dmauri/archive/2005/03/30/4278.aspx"&gt;blog &lt;/a&gt;from &lt;a href="http://weblogs.sqlteam.com/dmauri/"&gt;David Mauri &lt;/a&gt;on explaining the importance of a Data Access Layer.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=399999" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/taganov/archive/tags/ASP+.NET/default.aspx">ASP .NET</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Miscellaneous/default.aspx">Miscellaneous</category><category domain="http://weblogs.asp.net/taganov/archive/tags/SQL+Server+2000/default.aspx">SQL Server 2000</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Design+Patterns/default.aspx">Design Patterns</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Test+Driven+Development+_2800_TDD_2900_/default.aspx">Test Driven Development (TDD)</category></item><item><title>More on Implementing an Interface in the UI, or the 7-layer system.</title><link>http://weblogs.asp.net/taganov/archive/2005/04/05/397300.aspx</link><pubDate>Wed, 06 Apr 2005 02:30:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:397300</guid><dc:creator>taganov</dc:creator><author>taganov</author><slash:comments>11</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/taganov/rsscomments.aspx?PostID=397300</wfw:commentRss><comments>http://weblogs.asp.net/taganov/archive/2005/04/05/397300.aspx#comments</comments><description>&lt;p&gt;&lt;a href="http://www.mattberther.com/2005/01/000589.html"&gt;Matt Berther is doing something similar &lt;/a&gt;to what I was talking about in my &lt;A href="http://weblogs.asp.net/taganov/archive/2005/03/31/396562.aspx"&gt;previous post&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Where we differ is where to do the validation of the business rules. He's wrapping his IMyView in a class that knows how to perform the Business Rule validation. I didn't discuss my validation method in my last post, so I'll discuss it here:&lt;/p&gt; &lt;p&gt;Let's say I have a class that knows how to save an IPerson to a DataStore. The Save Method signature looks like:&lt;/p&gt; &lt;p&gt;Public Function Save(p as IPerson) as Boolean&lt;/p&gt; &lt;p&gt;If my Web page or UserControl implements IPerson, I can simply pass the entire page or UserControl to the Save method of my Data Access object. But where does the validation get done?&amp;nbsp; It gets done during the save attempt.&amp;nbsp; The UI layer does not have direct access to the Data Access layer. It has indirect access via a set of FACTORY objects. When the UI attempts to save, it does so through a FACTORY class that first performs validation of the Business Rules, and if everything is okay, continues with the save. If the validation fails, then a list of errors is returned to the UI via an event handler.&lt;/p&gt; &lt;p&gt;This separation of Business Rule Objects (policy) from Business Data Objects (data) and Data Access objects (persistence) allows for easily turning various business rules on and off. As new rules are added, old ones are removed, or existing ones change their optional status, only one layer has to be modified: the validation layer. Additionally, this layer can itself be stored in the&amp;nbsp;Data Store&amp;nbsp;and configured dynamically. The UI &lt;em&gt;can and should&lt;/em&gt; perform its own validation, but if it doesn't, no harm is done to the underlying system.&lt;/p&gt; &lt;p&gt;Consider the following example. Assume an interface IPerson defined as follows:&lt;/p&gt;&lt;font color="#0000ff" size="2"&gt;&lt;font color="#0000ff" size="2"&gt; &lt;p&gt;Public&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Interface&lt;/font&gt;&lt;font size="2"&gt;&lt;font color="#000000"&gt; IPerson&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; IsACompany() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Boolean&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; CompanyName() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; FirstName() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; MiddleName() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; LastName() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; Address() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; IAddress&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; Contact() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; IContactInformation&lt;/p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt; &lt;p&gt;End&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Interface&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&lt;/p&gt;&lt;/font&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt; &lt;p&gt;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Interface&lt;/font&gt;&lt;font size="2"&gt; IAddress&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; Address1() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; Address2() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; City() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; State() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; PostalCode() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/p&gt; &lt;p&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Interface&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Interface&lt;/font&gt;&lt;font size="2"&gt; IContactInformation&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; PhoneNumbers() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; System.Collections.Specialized.NameValueCollection&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Property&lt;/font&gt;&lt;font size="2"&gt; EmailAddresses() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; System.Collections.Specialized.NameValueCollection&lt;/p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt; &lt;p&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Interface&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&lt;/p&gt;&lt;/font&gt; &lt;p&gt;Consider the following Business Rules: In IPerson, the LastName is required if it is, if fact, a person. The CompanyName is required if the IPerson is a company (anybody have a better name for IPerson that supports it being either a human or a company?)&lt;/p&gt; &lt;p&gt;My FACTORY Object might look like this:&lt;/p&gt;&lt;font color="#0000ff" size="2"&gt; &lt;p&gt;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Class&lt;/font&gt;&lt;font size="2"&gt; FactoryObject&lt;/font&gt;&lt;font size="2"&gt;&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ReadOnly&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Property&lt;/font&gt;&lt;font size="2"&gt; SecurityContext() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; SystemUser&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&lt;font color="#008000"&gt;' snipped for brevity&lt;/font&gt;&lt;/font&gt; &lt;p&gt;&lt;font color="#0000ff" size="2"&gt;&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/font&gt;&lt;font size="2"&gt; Save(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; p &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; IPerson) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Boolean&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dim&lt;/font&gt;&lt;font size="2"&gt; V &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; IPersonValidator(p)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dim&lt;/font&gt;&lt;font size="2"&gt; pm &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; PersonManager(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Me&lt;/font&gt;&lt;font size="2"&gt;.SecurityContext)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Return&lt;/font&gt;&lt;font size="2"&gt; pm.Save(p, V)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/p&gt; &lt;p&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Class&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#0000ff" size="2"&gt;&lt;font color="#000000" size="3"&gt;The FACTORY Object simply instantiates the validator as a required parameter to the DataAccess Object's Save() method, hiding this complexity from the client code. &lt;/font&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&lt;font color="#000000" size="3"&gt;The Validator Object might look like this:&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;&lt;font color="#0000ff" size="2"&gt;&lt;font color="#0000ff" size="2"&gt; &lt;p&gt;Public&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Class&lt;/font&gt;&lt;font size="2"&gt;&lt;font color="#000000"&gt; IPersonValidator&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ReadOnly&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Property&lt;/font&gt;&lt;font size="2"&gt; Person() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; IPerson&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;' snipped for brevity&lt;/font&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#0000ff" size="2"&gt;&lt;/p&gt; &lt;p dir="ltr" style="MARGIN-RIGHT: 0px"&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ReadOnly&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Property&lt;/font&gt;&lt;font size="2"&gt; IsValid() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Boolean&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p dir="ltr" style="MARGIN-RIGHT: 0px"&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Get&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Return&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Me&lt;/font&gt;&lt;font size="2"&gt;.ValidateNames&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Get&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Property&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/font&gt;&lt;font size="2"&gt; ListErrors() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/font&gt;&lt;font size="2"&gt;()&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;' snipped for brevity&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#008000"&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Protected&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/font&gt;&lt;font size="2"&gt; ValidateNames() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Boolean&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dim&lt;/font&gt;&lt;font size="2"&gt; Result &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Boolean&lt;/font&gt;&lt;font size="2"&gt; = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;True&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Me&lt;/font&gt;&lt;font size="2"&gt;.Person.IsACompany &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Result = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Me&lt;/font&gt;&lt;font size="2"&gt;.Person.CompanyName.Trim &amp;lt;&amp;gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/font&gt;&lt;font size="2"&gt;.Empty&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Else&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Result = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Me&lt;/font&gt;&lt;font size="2"&gt;.Person.LastName.Trim &amp;lt;&amp;gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/font&gt;&lt;font size="2"&gt;.Empty&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Return&lt;/font&gt;&lt;font size="2"&gt; Result&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Sub&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt;(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; p &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; IPerson)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#008000"&gt;' snipped for brevity&lt;/font&gt;&lt;/font&gt; &lt;p&gt;&lt;font color="#0000ff" size="2"&gt;&lt;/p&gt;End&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Class&lt;/p&gt;&lt;/font&gt;&lt;/font&gt; &lt;p&gt;And finally, the Save() method on the Data Access object might look like:&lt;/p&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/font&gt;&lt;font size="2"&gt; Save(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; p &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; IPerson, &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; V &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; IPersonValidator) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Boolean&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dim&lt;/font&gt;&lt;font size="2"&gt; Result &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Boolean&lt;/font&gt;&lt;font size="2"&gt; = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;False&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;If&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Not&lt;/font&gt;&lt;font size="2"&gt; V.IsValid() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'Raise a ValidationError event&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'snipped for brevity&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Else&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Result = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Me&lt;/font&gt;&lt;font size="2"&gt;.SaveToDataStore(p) &lt;/font&gt;&lt;font color="#008000" size="2"&gt;' execute SQL to store IPerson in Database&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Return&lt;/font&gt;&lt;font size="2"&gt; Result&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#0000ff" size="2"&gt;&lt;font color="#000000" size="3"&gt;I'd be interested in any feedback from the community on this approach. This is something that I've started doing recently and so far it's working out rather nicely. There may be pitfalls that I can't see yet, so if you see any major problems with this, ping me :-). You can ping me if you like it too :-P.&lt;/font&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&lt;/p&gt;&lt;/font&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=397300" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/taganov/archive/tags/ASP+.NET/default.aspx">ASP .NET</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Miscellaneous/default.aspx">Miscellaneous</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Design+Patterns/default.aspx">Design Patterns</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Test+Driven+Development+_2800_TDD_2900_/default.aspx">Test Driven Development (TDD)</category></item><item><title>Culmination</title><link>http://weblogs.asp.net/taganov/archive/2005/03/31/396562.aspx</link><pubDate>Fri, 01 Apr 2005 02:26:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:396562</guid><dc:creator>taganov</dc:creator><author>taganov</author><slash:comments>3</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/taganov/rsscomments.aspx?PostID=396562</wfw:commentRss><comments>http://weblogs.asp.net/taganov/archive/2005/03/31/396562.aspx#comments</comments><description>&lt;p&gt;I've been reading about and applying Test Driven Development and Design Patterns over the last few months. Today I saw the culmination of that effort in the design of new module of ASP .NET software that was added to an existing project on Monday.&lt;/p&gt; &lt;p&gt;Early on I had created a generic object for leafing through pages of records in the database.&amp;nbsp; I called the object the RecordCursor, and implemented basic cursor and search functionality (MoveFirst, MoveNext, etc.). Separately, I had designed a generic object for managing records in the database--I called this a RecordManager. It has 3 basic functions (Save, SaveNew, and Delete), and sometimes has others (ListAll).&lt;/p&gt; &lt;p&gt;Of course, there are also the Business Objects themselves.&lt;/p&gt; &lt;p&gt;For any Business Object in the system, there are basically 3 parts--the business object itself, the Manager, and the Browser.&amp;nbsp; This gives me a good separation of the Business Layer from the Data Access Layer.&lt;/p&gt; &lt;p&gt;I wanted to make the UI solely dependent on Interfaces and Abstract classes. When building my DataAccess components, I made sure that they only returned Interfaces from any API function. I further made sure that none of the concrete objects were visible to the UI.&lt;/p&gt; &lt;p&gt;This is where it starts to get cool. The RecordManager classes receive only interfaces to their Save, SaveNew, and Delete functions. The idea I had for the UI was to develop a set of UserControls &lt;em&gt;that implemented the interface of the object they were intended to edit. &lt;/em&gt;Thus, if I have an interface IPerson, then I'll have a PersonEditorControl UserControl that implements IPerson. Instead of creating property declarations with internal variables, I'm redirecting the properties to the server controls embedded on the UserControl.&lt;/p&gt; &lt;p&gt;For Example:&lt;/p&gt; &lt;p&gt;Public Property FirstName as String Implements IPerson.FirstName&lt;/p&gt; &lt;p&gt;Get&lt;/p&gt; &lt;p&gt;return Me.txtFirstName.Text&lt;/p&gt; &lt;p&gt;End Get&lt;/p&gt; &lt;p&gt;Set (value as String)&lt;/p&gt; &lt;p&gt;Me.txtFirstName.Text = value&lt;/p&gt; &lt;p&gt;End Set&lt;/p&gt; &lt;p&gt;End Property&lt;/p&gt; &lt;p&gt;'etc...&lt;/p&gt; &lt;p&gt;Thus, the editor becomes an instance of the object it is intended to edit.&amp;nbsp; When I'm done editing, I simply pass the control in its entirety to RecordManager.Save(p as IPerson).&lt;/p&gt; &lt;p&gt;I've been writing code for about 5 years now, and haven't seen anybody doing this in any existing code, nor in any books. It's worked out pretty well for the project I'm working on, so I thought I'd share.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=396562" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/taganov/archive/tags/ASP+.NET/default.aspx">ASP .NET</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Design+Patterns/default.aspx">Design Patterns</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Test+Driven+Development+_2800_TDD_2900_/default.aspx">Test Driven Development (TDD)</category></item><item><title>For an Introduction to TDD...</title><link>http://weblogs.asp.net/taganov/archive/2005/03/01/383297.aspx</link><pubDate>Wed, 02 Mar 2005 03:38:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:383297</guid><dc:creator>taganov</dc:creator><author>taganov</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/taganov/rsscomments.aspx?PostID=383297</wfw:commentRss><comments>http://weblogs.asp.net/taganov/archive/2005/03/01/383297.aspx#comments</comments><description>&lt;p&gt;&lt;a href="http://sdmagazine.com/columnists/martin/"&gt;Click here&lt;/a&gt;. It's a column by Robert C. Martin that illustrates how TDD improves the quality of code.&lt;/p&gt; &lt;p&gt;This line relates directly to my last post, "Tests are users, too. The needs of the tests are often the same as the needs of the real users."&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=383297" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/taganov/archive/tags/Miscellaneous/default.aspx">Miscellaneous</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Design+Patterns/default.aspx">Design Patterns</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Test+Driven+Development+_2800_TDD_2900_/default.aspx">Test Driven Development (TDD)</category></item><item><title>An Unexpected Benefit of TDD</title><link>http://weblogs.asp.net/taganov/archive/2005/03/01/382687.aspx</link><pubDate>Tue, 01 Mar 2005 21:58:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:382687</guid><dc:creator>taganov</dc:creator><author>taganov</author><slash:comments>5</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/taganov/rsscomments.aspx?PostID=382687</wfw:commentRss><comments>http://weblogs.asp.net/taganov/archive/2005/03/01/382687.aspx#comments</comments><description>&lt;p&gt;I've recently begun using Test Driven Development as my method for developing and maintaining applications. A worry I had early on was that I have inherited several poorly designed ASP .NET applications (read "poorly designed" as "not designed") and that it would be difficult to use TDD in that environment.&lt;/p&gt; &lt;p&gt;I was mistaken.&lt;/p&gt; &lt;p&gt;Today I had an issue with a browse page. The code to browse the database was slammed behind the page in a 250-300 line sub routine. Additionally, the same code was being used by 5 different buttons on the page--btnFirst, btnPrevious, btnNext, btnLast, and btnSearch. Given that the page is supposed to allow the user to browse the records a "page" (a set of x number of records) at a time, I thought the ITERATOR pattern most appropriate. I wrote a unit-test for the MoveFirst(pageSize) functionality of the RecordBrowser class, which didn't yet exist.&lt;/p&gt; &lt;p&gt;After implementing the MoveFirst() method, I then went on and wrote tests for--and coded--the MoveNext(), MovePrevious(), MoveLast(), and MoveToSpecificRecord() methods.&amp;nbsp; After writing the tests and verifying that they worked as expected, it was pretty simple to plug the new class behind the page and start using it instead of the existing code. I watched 250+ lines of code in the Code-Behind dwindle to less than 25 lines.&lt;/p&gt; &lt;p&gt;Because I needed to write a test to capture the failure (which ultimately was a failure in a stored procedure call), I had to create an object that I could test in isolation from the UI and surrounding logic.&amp;nbsp; This took a ltitle bit longer up front, but because I had separated out the actual db browsing logic from the UI, I could just work on getting that part to work correctly without having to worry about whether other stuff in the page was messing up the needs of the browing class.&amp;nbsp;I also ended up with a set of new interfaces and abstract classes that I'll be able to re-use when I have to fix errors with other variants of the browser class, which means that the same fix will go much faster next time around.&lt;/p&gt; &lt;p&gt;When it came time to integrate the new class into the existing page, it took hardly any time at all. I had a couple of initial errors, but since I had already tested the browsing object, I knew that these errors were related to things on the page--not the browsing class. Once I got those straightened out, it was a snap to run the program and watch the page browse the db flawlessly. &lt;/p&gt; &lt;p&gt;So now I have a poorly designed web application with one lone piece of well-designed code. It took less than a morning to add the db browser class, and now I'll know that it's still functional every time I work on this program. The reason that I was able to seamlessly insert a piece of well-designed code into the system is because of the nature of TDD--the whole point is to test functionality &lt;em&gt;in isolation&lt;/em&gt;. This means that design changes (and additions) are &lt;em&gt;incremental&lt;/em&gt;, and have little impact on the larger system (or non-system in this case).&amp;nbsp; BUT--and this is the key point--&lt;em&gt;over time, &lt;/em&gt;as bits of quality design are added here and there, the overall quality of the system &lt;em&gt;incrementally&lt;/em&gt; improves.&amp;nbsp; As more and more features and issues are added or fixed, the overall system will begin to take on a definite structured design.&amp;nbsp; TDD causes the design to spread and overtake the system.&lt;/p&gt; &lt;p&gt;I am &lt;em&gt;so&lt;/em&gt; looking forward to new enhancements on these old projects I inherited.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=382687" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/taganov/archive/tags/ASP+.NET/default.aspx">ASP .NET</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Miscellaneous/default.aspx">Miscellaneous</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Design+Patterns/default.aspx">Design Patterns</category><category domain="http://weblogs.asp.net/taganov/archive/tags/Test+Driven+Development+_2800_TDD_2900_/default.aspx">Test Driven Development (TDD)</category></item></channel></rss>