<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0"><channel><title>Steve Smith's Blog</title><link>http://stevesmithblog.com/</link><description>Musings on Software and the Developer Community</description><generator>Graffiti CMS 1.2 (build 1.2.0.2308)</generator><lastBuildDate>Thu, 22 Oct 2009 03:53:37 GMT</lastBuildDate><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/StevenSmith" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><title>A First Pass at PotterKata</title><link>http://stevesmithblog.com/blog/a-first-pass-at-potterkata/</link><pubDate>Thu, 22 Oct 2009 03:53:37 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/a-first-pass-at-potterkata/</guid><dc:creator>ssmith</dc:creator><slash:comments>2</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;p&gt;Tonight at &lt;a href="http://HudsonSC.com/"&gt;Hudson Software Craftsmanship&lt;/a&gt;, I paired with another group member and worked on the &lt;a href="http://codingdojo.org/cgi-bin/wiki.pl?KataPotter"&gt;PotterKata&lt;/a&gt; for the first time.&amp;#160; I’d seen &lt;a href="http://www.iamnotmyself.com/2009/10/20/UsingMSpecToSolveKataPotterPart1TheSpecifications.aspx"&gt;NotMyself write about it&lt;/a&gt; a few days ago, which prompted me to suggest it for the group to work on (&lt;a href="http://hudsonsc.com/meetings/october-2009-meeting-recap/"&gt;summary of the meeting here&lt;/a&gt;).&lt;/p&gt;  &lt;p&gt;Briefly, this kata is a fairly real-world exercise in that it has to do with business rules for a shopping cart that are non-linear.&amp;#160; In this case, the rules are:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;One copy of any of the five books costs 8 EUR. If, however, you buy two different books from the series, you get a 5% discount on those two books. If you buy 3 different books, you get a 10% discount. With 4 different books, you get a 20% discount. If you go the whole hog, and buy all 5, you get a huge 25% discount. &lt;/p&gt;    &lt;p&gt;Note that if you buy, say, four books, of which 3 are different titles, you get a 10% discount on the 3 that form part of a set, but the fourth book still costs 8 EUR. &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Attentive readers will notice that there is no 15% discount – there’s a big jump from 3 different titles (10%) to 4 (20%).&amp;#160; Because of this, it’s possible to calculate the discount incorrectly (that is, not optimized for the buyer), with a cart that includes 2 of each of 3 titles and 1 of each of the remaining 2.&amp;#160; If you make it a set of 5 (25% off) and a set of 2 (5% off) your price will be higher than if you make it 2 sets of 4 (20% off all of them).&lt;/p&gt;  &lt;p&gt;My partner and I set about TDDing this with these tests (the first 3 are easy so I left out the actual implementation):&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;div&gt;   &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;     &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; EmptyCartShouldCostZero()&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;...&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; CartWithOneBookShouldCostEightEuros()&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;...&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; CartWithTwoOfSameBookShouldCost16Euros()&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;...&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; CartWithTwoDifferentBooksShouldCost1520Euros()&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Arrange&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    var cart = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Cart();&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    var book = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Book(&lt;span style="color: #006080"&gt;&amp;quot;One&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    var book2 = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Book(&lt;span style="color: #006080"&gt;&amp;quot;Two&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    cart.AddBookToCart(book);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    cart.AddBookToCart(book2);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Act&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    var totalPrice = cart.GetTotalPrice();&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;const&lt;/span&gt; &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; expectedCost = 2 * 8.0m * .95m;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Assert&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    Assert.AreEqual(expectedCost, totalPrice);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;At this point it’s possible to do the calculation work simply based on the total number of distinct titles in the cart.&amp;#160; However, the next test is much harder:&lt;/p&gt;

&lt;div&gt;
  &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;
    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; PriceCalculatorYields2320With2SameAnd1DifferentBooks()&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Arrange&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    var books = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; List&amp;lt;CartItem&amp;gt;()&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                    {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                        &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CartItem(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Book(&lt;span style="color: #006080"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;), 2),&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                        &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CartItem(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Book(&lt;span style="color: #006080"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;), 1)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                    };&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Act&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    var totalPrice = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; PriceCalculator().CalculatePrice(books);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Assert&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    Assert.AreEqual(23.20m, totalPrice);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Note that at this point we moved the logic of calculating discounts into its own class, which we pass into our Cart (but which we can test in isolation from the cart, as shown here).&amp;#160; This test required substantial rework of the calculation bits, because it was no longer sufficient to just multiply the total book quantity times the discount based on distinct titles.&lt;/p&gt;

&lt;p&gt;The last test is the hardest one, and represents the acceptance test given in the Kata itself:&lt;/p&gt;

&lt;div&gt;
  &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;
    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; PriceCalculatorYields5120WithGivenCartLoadOfBooks()&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Arrange&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    var books = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; List&amp;lt;CartItem&amp;gt;()&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                    {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                        &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CartItem(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Book(&lt;span style="color: #006080"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;), 2),&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                        &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CartItem(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Book(&lt;span style="color: #006080"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;), 2),&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                        &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CartItem(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Book(&lt;span style="color: #006080"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;), 2),&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                        &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CartItem(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Book(&lt;span style="color: #006080"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;), 1),&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                        &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CartItem(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Book(&lt;span style="color: #006080"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;), 1)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                    };&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Act&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    var totalPrice = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; PriceCalculator().CalculatePrice(books);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Assert&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    Assert.AreEqual(51.20m, totalPrice);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;We were tight on time and one of the few pairs to get a working solution, so this code is still quite rough and would need additional tests and refactoring, but it works for this specific case.&amp;#160; The main logic of the problem ended up all being in the PriceCalculator class shown below:&lt;/p&gt;

&lt;div&gt;
  &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;
    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; PriceCalculator : IPriceCalculator&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;const&lt;/span&gt; &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; BOOKPRICE = 8.0m;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; CalculatePrice(IEnumerable&amp;lt;CartItem&amp;gt; items)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        List&amp;lt;Book&amp;gt; bookSet = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; List&amp;lt;Book&amp;gt;();&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        var totalPrice = 0.0m;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;do&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            bookSet = GetUniqueBookSet(items);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            totalPrice += CalculateUniqueSetPrice(bookSet);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        } &lt;span style="color: #0000ff"&gt;while&lt;/span&gt; (bookSet.Count &amp;gt; 0);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; totalPrice;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; List&amp;lt;Book&amp;gt; GetUniqueBookSet(IEnumerable&amp;lt;CartItem&amp;gt; items)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        var bookSet = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; List&amp;lt;Book&amp;gt;();&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        var itemsOrderedByCount = items.OrderByDescending(x =&amp;gt; x.Quantity).Where(x =&amp;gt; x.Quantity &amp;gt; 0);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (var item &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; itemsOrderedByCount)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (items.ToList().Count &amp;gt; 3)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (item.Quantity &amp;gt; 1)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                    AddBookToSet(item, bookSet);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (bookSet.Count == 3 &amp;amp;&amp;amp; item.Quantity == 1)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                    AddBookToSet(item, bookSet);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (bookSet.Count &amp;gt; 0) {&lt;span style="color: #0000ff"&gt;return&lt;/span&gt; bookSet;}&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (var item &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; itemsOrderedByCount)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (item.Quantity &amp;gt; 0)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                AddBookToSet(item, bookSet);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; bookSet;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; AddBookToSet(CartItem item, List&amp;lt;Book&amp;gt; bookSet)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        bookSet.Add(item.Book);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        item.Quantity--;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; CalculateUniqueSetPrice(IEnumerable&amp;lt;Book&amp;gt; books)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        var count = books.Count();&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        var discountMultiple = 1.0m;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;switch&lt;/span&gt; (count)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        {&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; 2:&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                discountMultiple = 0.95m;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; 3:&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                discountMultiple = 0.90m;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; 4:&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                discountMultiple = 0.80m;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; 5:&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                discountMultiple = 0.75m;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; count * discountMultiple * BOOKPRICE;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;I’m pretty happy with the CalculateUniqueSetPrice() method; it’s clean and easy to follow.&amp;#160; Likewise, CalculatePrice() is pretty easy to follow as well, though I’m not sure it’s as clear as it could be at expressing what it’s doing, which is pulling out sets of unique book titles and pricing each one.&amp;#160; The actual logic for how it does the grouping of books into sets is in the big ugly GetUniqueBookSet method, which is full of magic numbers and inscrutable nested if statements.&amp;#160; It’s nasty.&amp;#160; If the discount schedule were to change such that the biggest jump in the discount schedule was not between 3 and 4 different titles, this method would break bigtime.&amp;#160; But it works.&lt;/p&gt;

&lt;p&gt;Note that we only ever group our collection of titles into sets once.&amp;#160; This program has no intelligence or built-in optimization – that was all done by humans (we figured out that the magic number 3 was the threshold value).&amp;#160; The more interesting problem to solve is one which works for any given discount schedule, by grouping the titles into sets in a variety of ways and seeing which resulted in the best price.&amp;#160; One other pair did something close to this and completed the problem, and at least one other pair was working on a brute force approach that would literally try every possible set combination to see which resulted in the best price.&lt;/p&gt;

&lt;p&gt;A simple way to discover the minimum optimal number of books to have per set (4 in this case), one could run through the discount schedule looking for the biggest gaps/differences.&amp;#160; With the given schedule, the difference of 10% when going from 3 titles to 4 titles would be the largest (every other difference is only 5%).&amp;#160; This calculation could be inserted to replace the magic number 3 in my implementation.&lt;/p&gt;

&lt;p&gt;I’d also want to move from foreach() and nested ifs to LINQ and lamba expressions.&amp;#160; I spent a little time trying this approach, but couldn’t get some of the syntax to work so ended up falling back to the familiarity of foreach() and if().&amp;#160; Still another option, not exclusive of this approach, would be to create a BookSet class which was capable of calculating its price, which would eliminate one of the responsibilities of PriceCalculator, which currently violates SRP because it is responsible for grouping books into sets as well as pricing such sets.&lt;/p&gt;

&lt;p&gt;Anyway, it’s a fun kata if you want to give it a shot.&amp;#160; If I have time to do it again on my own and to arrive at a more elegant solution, I’ll be sure to post it.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/ppfitaTZKKM" height="1" width="1"/&gt;</description></item><item><title>Which Visual Studio 2010 is Team Suite?</title><link>http://stevesmithblog.com/blog/which-visual-studio-2010-is-team-suite/</link><pubDate>Wed, 21 Oct 2009 13:02:00 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/which-visual-studio-2010-is-team-suite/</guid><dc:creator>ssmith</dc:creator><slash:comments>1</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;p&gt;The new VS 2010 has a new lineup of versions which you can find described on the &lt;a href="http://www.microsoft.com/visualstudio/en-us/products/2010/default.mspx#compare"&gt;Visual Studio 2010 Products page&lt;/a&gt;.&amp;#160; Some things to note:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;em&gt;Ultimate&lt;/em&gt; is the new &lt;em&gt;Suite&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;The “Data Dude” SKU is now fully incorporated in the Premium and Ultimate versions&lt;/li&gt;    &lt;li&gt;Premium and Ultimate come with a production license for Expression Studio 3, as well as Visio and Project 2010.&lt;/li&gt;    &lt;li&gt;All versions now come with some Windows Azure pieces for dev/test use&lt;/li&gt;    &lt;li&gt;All versions ship with Team Foundation Server (which now &lt;a href="http://blogs.msdn.com/bharry/archive/2009/10/01/tfs-2010-for-sourcesafe-users.aspx"&gt;supports running on the client as a lightweight alternative to a full server-side install&lt;/a&gt;), providing version control, build automation, bug tracking, and continuous automation out of the box very quickly&lt;/li&gt; &lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/dw9f8nYRQ4A" height="1" width="1"/&gt;</description></item><item><title>How to Install Windows 7 from USB Drive</title><link>http://stevesmithblog.com/blog/how-to-install-windows-7-from-usb-drive/</link><pubDate>Wed, 14 Oct 2009 16:32:24 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/how-to-install-windows-7-from-usb-drive/</guid><dc:creator>ssmith</dc:creator><slash:comments>13</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 0px 10px; display: inline; border-top: 0px; border-right: 0px" title="windows7" border="0" alt="windows7" align="right" src="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/HowtoInstallWindows7fromUSBDrive_B067/windows7_3.jpg" width="180" height="121" /&gt; I decided to reinstall Win7 on one of my laptops because it was acting up – turns out that’s not helping and I think at this point it’s a hardware problem (either memory or hard drive – I’m going to try memory next).&amp;#160; In the course of troubleshooting the problem, I decided to rule out a bad installer DVD for Windows 7 (the installer was failing, saying it couldn’t access certain required files).&amp;#160; So I created a USB installer for Windows 7 x64.&amp;#160; And since I’ve been meaning to install Win7 on my Dell mini10 for a while (which has no CD/DVD reader), I also created a separate USB installer for it for Win7 32-bit x86.&lt;/p&gt;  &lt;p&gt;I found &lt;a href="http://www.blogsdna.com/2016/how-to-install-windows-7-from-usb-drive-without-windows-7-iso-dvd.htm"&gt;this post to be helpful, but it has some problems in the original post that are corrected in the comments&lt;/a&gt;.&amp;#160; However, there are about a million comments so to save you the trouble of reading them all and trying to figure out which are correct, let me just post what worked for me here.&amp;#160; I’m sure this is not the only way to create a USB thumb / flash drive installer for Windows 7, but it is one that I can confirm works.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Requirements&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;You need a 4GB or larger USB drive.&amp;#160; &lt;/p&gt;  &lt;p&gt;You also need the 32-bit ISO for Windows 7 no matter what, and you need the 64-bit ISO if you’re planning on making a 64-bit installer.&amp;#160; (I’m assuming you need the 32-bit ISO in order to run the bootsect command correctly from a 32-bit machine based on a comment I read.&amp;#160; I haven’t tried creating an installer from a 64-bit machine nor have I tried running the 64-bit ISO’s bootsect command, so I can’t say how or if those methods work).&amp;#160; &lt;/p&gt;  &lt;p&gt;I performed all of these steps on a 32-bit Windows Vista installation.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Creating a Windows 7 USB Drive Installer&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;1. Download &lt;a href="http://mbrwizard.com/download.shtml"&gt;MBRWiz&lt;/a&gt; and unzip it to some folder.&lt;/p&gt;  &lt;p&gt;2. &lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 0px 10px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" align="right" src="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/HowtoInstallWindows7fromUSBDrive_B067/image_thumb.png" width="143" height="244" /&gt;Insert your USB drive, make sure there’s nothing on it you need (it’s about to all be destroyed), and Format it.&amp;#160; If you’re in Vista you simply right-click on it and select Format, choosing NTFS as the file system.&lt;/p&gt;  &lt;p&gt;3. Mount your Windows 7 32-bit ISO as a drive (using &lt;a href="http://www.magiciso.com/tutorials/miso-magicdisc-overview.htm"&gt;MagicDisc&lt;/a&gt; or something similar).&lt;/p&gt;  &lt;p&gt;4. Open up a cmd windows with administrator privileges.&amp;#160; The easiest way to do this is to go to Start &amp;gt; Run and type ‘cmd’ and then press ctrl-shift-enter (instead of just enter).&lt;/p&gt;  &lt;p&gt;5. Change to the folder where you unzipped MBRWiz and run the following commands:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;mbrwiz /list (note the disk number of your USB drive)&lt;/p&gt;    &lt;p&gt;mbrwiz /disk=X /active=1&amp;#160; (replace X with the number of your USB drive)&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;In my screenshot below you can see that my disk number for my USB drive is 1, so I used /disk=1 /active=1 for my command.&lt;/p&gt;  &lt;p&gt;6. Now in the same cmd window, change to your ISO mounted drive (in my case the F drive).&amp;#160; Type the following commands, replacing e: with the actual drive letter of your USB drive:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;cd boot&lt;/p&gt;    &lt;p&gt;bootsect /nt60 e:&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Your command window should read more-or-less like the following screenshot (click for full size):&lt;/p&gt;  &lt;p&gt;&lt;a href="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/HowtoInstallWindows7fromUSBDrive_B067/image_4.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/HowtoInstallWindows7fromUSBDrive_B067/image_thumb_1.png" width="340" height="280" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;7. Last, copy all of the files from your ISO mounted drive to your USB drive.&amp;#160; &lt;strong&gt;If you are creating a 64-bit installer, unmount the 32-bit ISO first, then mount the 64-bit ISO and copy the files from there.&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;I just followed these steps, twice, to create one 32-bit and one 64-bit Windows 7 USB thumb drive installers.&amp;#160; I can confirm that these steps work for me doing all of the creation work from a 32-bit Vista machine.&amp;#160; I can’t attest to any other configuration or process’s likelihood to work or not, but feel free to comment if you have suggestions, improvements, or success stories to share.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/PnLGf2UXzVI" height="1" width="1"/&gt;</description></item><item><title>Avoid Entrenched Dependencies</title><link>http://stevesmithblog.com/blog/avoid-entrenched-dependencies/</link><pubDate>Tue, 13 Oct 2009 15:51:00 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/avoid-entrenched-dependencies/</guid><dc:creator>ssmith</dc:creator><slash:comments>1</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;p&gt;Last year I wrote about &lt;a href="http://stevesmithblog.com/blog/avoiding-dependencies/"&gt;Avoiding Dependencies&lt;/a&gt; and described some &lt;a href="http://stevesmithblog.com/blog/insidious-dependencies/"&gt;Insidious Dependencies&lt;/a&gt; (with help from many commenters) that many developers might not immediately recognize as dependencies.&amp;#160; It occurred to me today that I should point out that dependencies themselves are not intrinsically bad design – all software has dependencies.&amp;#160; The important distinction here that I think is a best practice is that one should make efforts to avoid &lt;em&gt;&lt;strong&gt;entrenched dependencies &lt;/strong&gt;&lt;/em&gt;on things that are &lt;strong&gt;&lt;em&gt;likely to change&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;  &lt;p&gt;As an example, I write most of my software using Microsoft .NET.&amp;#160; I’m inherently taking a dependency on the CLR.&amp;#160; If at some point the customer’s needs dictate that I need to move to Java or Ruby, that is most likely going to be a painful thing.&amp;#160; However, I don’t foresee such a change being required, so I’m happy to have tight coupling to .NET for most of my applications.&lt;/p&gt;  &lt;p&gt;When you perform a code review, whether for your own software or for someone else’s, one of the things you should analyze are the dependencies the code has, and whether these are implicit or explicit and whether they are entrenched or flexible.&amp;#160; If the software simply instantiates classes directly wherever it might need them, and makes no use of Dependency Injection, it’s likely to be riddled with &lt;em&gt;implicit&lt;/em&gt; and &lt;em&gt;entrenched&lt;/em&gt; dependencies.&amp;#160; On the other hand, if classes identify their dependencies in their constructor via interfaces, and an IoC container is in place to allow easy management of the actual implementation classes that are used at runtime (that conform to these interfaces), then the dependencies are &lt;em&gt;explicit&lt;/em&gt; and &lt;em&gt;flexible&lt;/em&gt;.&amp;#160; (and on the &lt;em&gt;other&lt;/em&gt; other hand, if &lt;em&gt;everything&lt;/em&gt; is an interface, the application is likely to be more difficult to understand than necessary – see below)&lt;/p&gt;  &lt;p&gt;There are costs associated with abstracting dependencies.&amp;#160; In the extreme, every “new” in your code is a dependency on a particular implementation, and thus could be replaced with an interface and injected in.&amp;#160; In practice, the main things you want to worry about abstracting are the ones that are &lt;em&gt;most likely to change&lt;/em&gt;, and over which you have the least control.&amp;#160; &lt;strong&gt;Things that need to change in order to test your code count as things that are likely to change,&lt;/strong&gt; and thus should always be abstracted if you plan on writing good unit tests!&amp;#160; Otherwise, don’t go overboard.&amp;#160; Wait until your dependence on a particular implementation causes pain before you start extracting interfaces and DI-ing them into all of your classes.&amp;#160; All things in moderation.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/Rw7PWYHCWKk" height="1" width="1"/&gt;</description></item><item><title>N Tier Design Lessons Learned Part 1</title><link>http://stevesmithblog.com/blog/n-tier-design-lessons-learned-part-1/</link><pubDate>Mon, 12 Oct 2009 15:54:00 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/n-tier-design-lessons-learned-part-1/</guid><dc:creator>ssmith</dc:creator><slash:comments>2</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;p&gt;&lt;a href="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/NTierDesignLessonsLearnedPart1_134E3/ScottsdalePrincessResort1_2.jpg"&gt;&lt;img style="border-right-width: 0px; margin: 0px 0px 0px 10px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="ScottsdalePrincessResort1" border="0" alt="ScottsdalePrincessResort1" align="right" src="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/NTierDesignLessonsLearnedPart1_134E3/ScottsdalePrincessResort1_thumb.jpg" width="244" height="184" /&gt;&lt;/a&gt; Eight years ago this month I gave my first presentation at a conference.&amp;#160; It was &lt;a href="http://devconnections.com/"&gt;DevConnections’&lt;/a&gt; Fall 2001 show, and it was held in Scottsdale, Arizona at the Princess Resort.&amp;#160; The show was delayed a couple of weeks from its originally scheduled dates, and took place Sep 30 to Oct 3rd, as a result of the events of 11 September 2001.&amp;#160; I still vividly remember the alarm with which my seatmate on the flight over (an older lady) observed that they had given us all plastic knives with our meals.&amp;#160; I reassured her, “It’s OK, we all have them.”&lt;/p&gt;  &lt;p&gt;As a result of the times and the rescheduling, the resort was a ghost town and the conference attendance was, shall we say, light.&amp;#160; I think my 3 sessions pulled in attendance numbers of 6, 10, and 6, which I recall being pretty happy with as not all speakers managed to get double digit attendance.&amp;#160; One of my sessions was on N-Tier Development in .NET.&amp;#160; You can still &lt;a href="http://aspsmith.com/DesktopDefault.aspx?tabindex=3&amp;amp;tabid=9"&gt;download all of my 2001-2003 talks on my old training web site here&lt;/a&gt;.&amp;#160; The talk which is the topic of this series is this one:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://aspsmith.com/DesktopModules/ViewDocument.aspx?DocumentID=17"&gt;N-Tier Development in .NET&lt;/a&gt; (rename it to NTier.zip or something similar)&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Lessons Learned&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;When I gave this talk, it was based on personal experience and a lot of industry best practices that I had picked up from other software professionals and from Microsoft itself.&amp;#160; These included things like the IBuySpy samples and FMStocks samples, which in their time were &lt;em&gt;amazing&lt;/em&gt; and wonderful and helped a ton of developers build effective and no doubt profitable applications using ASP.NET.&amp;#160; &lt;/p&gt;  &lt;p&gt;However, now I’m 8 years older and wiser, and there are things I certainly would change about how I recommended architecting applications in .NET in 2001.&amp;#160; I know that millions of developers learned these same best practices, and that probably millions of developers continue to believe these remain best practices for building applications today.&amp;#160; I’ve learned otherwise, and I am hoping to show through this series both why I’ve changed the way I do things and how to go about changing a .NET Solution from what I’ll refer to as “traditional n-tier” architecture to a more domain-driven or &lt;a href="http://jeffreypalermo.com/blog/the-onion-architecture-part-1/"&gt;onion architecture&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;The Original Presentation&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Unzip the file into a folder and you should see something like this:&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/NTierDesignLessonsLearnedPart1_134E3/image_3.png" width="523" height="168" /&gt; &lt;/p&gt;  &lt;p&gt;If you look at the slide deck (which I would totally redo today, naturally), you’ll see Slide 9 shows this diagram:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/NTierDesignLessonsLearnedPart1_134E3/image_2.gif"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/NTierDesignLessonsLearnedPart1_134E3/image_thumb.gif" width="502" height="311" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;This was always one of my favorite slides in this talk.&amp;#160; I believe I got this diagram from MSDN – I know I didn’t create it myself (slide 10 shows the extent of my diagram creation skills at the time).&amp;#160; N-tier architecture was actually kind of novel back in 2001.&amp;#160; there were plenty of developers in the audience who weren’t familiar with the concept or hadn’t bought into it yet.&amp;#160; Today, it seems like most developers I talk to are certainly familiar with this separation into layers, and I would say that most mainstream .NET developers in the last few years are following a Two- or Three- tier architecture.&amp;#160; However, only a minority of them create their solutions in such a way that the Data Access Layer assembly is not referenced directly by the Business Logic assembly, which in turn is referenced directly by the Presentation Layer.&amp;#160; This is an &lt;a href="http://stevesmithblog.com/blog/principles-patterns-and-practices-of-mediocre-programming/"&gt;anti-pattern (Data Driven Architecture)&lt;/a&gt;, and is one of the lessons learned that I will cover in the next part of this series.&lt;/p&gt;  &lt;p&gt;I’ll also include a download of a slightly cleaned up version of the sample code, updated to use a VS2008 solution file and including the schema needed for the User table, to save anyone wishing to follow along in the code the tedium of such things.&amp;#160; But I wanted to point to the actual, original file so that you could see it in all its glory, warts and all, and see how far we’ve come in the last 8 years.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/dLek9dTkqDY" height="1" width="1"/&gt;</description></item><item><title>Find String in Files with Given Extension Using PowerShell</title><link>http://stevesmithblog.com/blog/find-string-in-files-with-given-extension-using-powershell/</link><pubDate>Sun, 11 Oct 2009 17:56:56 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/find-string-in-files-with-given-extension-using-powershell/</guid><dc:creator>ssmith</dc:creator><slash:comments>2</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;p&gt;This week I found myself wanting to search within files of a given extension for a particular substring.&amp;#160; I often find myself missing UNIX’s grep tool.&amp;#160; In any event, I tried using the default Windows Vista file search dialog, but found that if I wanted to search for “connection” or “database” within all files ending with “.cs” or “.config” I was unable to do so.&amp;#160; I’m guessing there actually *is* a way to do this from the GUI, but after spending a couple of minutes either searching all files for the text “.cs” or else searching for files named “connection” I opted to just do it from the command line using PowerShell.&lt;/p&gt;  &lt;p&gt;PowerShell is just freaking amazing.&amp;#160; Seriously.&amp;#160; Being able to easily store collections of objects in ad-hoc variables (named $something) and then to further be able to pipe the output of anything to anything (using | just like most other shells), you can quickly do some amazing stuff.&amp;#160; I use &lt;a href="http://stevesmithblog.com/blog/copy-pictures-to-folders-by-date-taken-with-powershell"&gt;PowerShell all the time now to make downloading pictures from my camera and organizing them by date easy&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/FindStringinFileswithGivenExtensionUsing_C437/image_2.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 10px 10px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" align="right" src="http://stevesmithblog.com/files/media/image/WindowsLiveWriter/FindStringinFileswithGivenExtensionUsing_C437/image_thumb.png" width="244" height="83" /&gt;&lt;/a&gt; To get a list of all files with a given extension that contain a given string, you can use the following steps (click on the screenshot to see it full size).&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Move to the appropriate folder.&amp;#160; While not strictly necessary, this will make your life easier.&amp;#160; The standard “cd” command from DOS works for this, but remember you can’t do “cd\” you have to do “cd \” to move to the root of the current volume.&lt;/li&gt;    &lt;li&gt;Get a list of all files.&amp;#160; This isn’t strictly required but again using an intermediate step like this can ensure you catch any mistakes in your operation earlier.&amp;#160; To get a collection of all the files in and below your current folder, use: &lt;strong&gt;$AllFiles = Get-ChildItem –recurse&lt;/strong&gt;&lt;/li&gt;    &lt;li&gt;Next we’ll filter our list to only include files with the extension we want.&amp;#160; If we’re looking for all of our C# source files, we would search for extensions that end with “.cs”.&amp;#160; Note that the $_ value refers to the current item in the collection as we loop through them applying our filter condition.&amp;#160; The “-eq” means “equals”.&amp;#160; The full command to create a new collection named $CSharpFiles is: &lt;strong&gt;$CSharpFiles = $AllFiles | where {$_.extension –eq “.cs” }&lt;/strong&gt;&lt;/li&gt;    &lt;li&gt;The next step is again not strictly necessary since we could simply write out the command, but it does show another useful PowerShell feature, namely the ability to alias commands with shorthand strings.&amp;#160; For example, the Select-String command is useful for matching files that contain a particular string, but if you’d prefer to just type “ss” for this, you can alias it like so: &lt;strong&gt;Set-Alias ss Select-String&lt;/strong&gt;&lt;/li&gt;    &lt;li&gt;Finally, we’ll use the ss alias on our filtered list of files to find all files containing the string “SqlConnection” which might be useful if you’re trying to ensure nobody is using ADO.NET when they should be using your ORM: &lt;strong&gt;ss SqlConnection $CSharpFiles | format-table Path&lt;/strong&gt;&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;The result will be the paths of all files that contain that string.&amp;#160; If you leave off Path from the end of the command, your table will include the line number and the actual Line of the file that matched the string you specified.&lt;/p&gt;  &lt;p&gt;So there you go.&amp;#160; It took much longer to write this blog post than to figure all of this out in PowerShell and find the files I was after.&amp;#160; Here are a couple of links that helped me get here:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://blogs.msdn.com/powershell/archive/2008/03/23/select-string-and-grep.aspx"&gt;Select-String and Grep&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.computerperformance.co.uk/ezine/ezine133.htm"&gt;PowerShell Script to List Files&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/KDGkRb85GV4" height="1" width="1"/&gt;</description></item><item><title>Don’t Repeat Yourself</title><link>http://stevesmithblog.com/blog/don-rsquo-t-repeat-yourself/</link><pubDate>Sun, 11 Oct 2009 03:12:44 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/don-rsquo-t-repeat-yourself/</guid><dc:creator>ssmith</dc:creator><slash:comments>3</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;h3&gt;&lt;font size="2"&gt;&lt;em&gt;(this is a submission I made to the upcoming &lt;/em&gt;&lt;/font&gt;&lt;a href="http://programmer.97things.oreilly.com/wiki/index.php/97_Things_Every_Programmer_Should_Know"&gt;&lt;font size="2"&gt;&lt;em&gt;97 Things Every Programmer Should Know&lt;/em&gt;&lt;/font&gt;&lt;/a&gt;&lt;font size="2"&gt;&lt;em&gt; book)&lt;/em&gt;&lt;/font&gt;&lt;/h3&gt;  &lt;p&gt;Of all the principles of programming, Don't Repeat Yourself (DRY) is perhaps one of the most fundamental. The principle was formulated by Andy Hunt and Dave Thomas in &lt;i&gt;The Pragmatic Programmer&lt;/i&gt;, and underlies many other well-known software development best practices and design patterns. The developer who learns to recognize duplication, and understands how to eliminate it through appropriate practice and proper abstraction, can produce much cleaner code than one who continuously infects the application with unnecessary repetition. &lt;/p&gt;  &lt;p&gt;&lt;b&gt;Duplication is waste&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Every line of code that goes into an application must be maintained, and is a potential source of future bugs. Duplication needlessly bloats the codebase, resulting in more opportunities for bugs and adding accidental complexity to the system. The bloat that duplication adds to the system also makes it more difficult for developers working with the system to fully understand the entire system, or to be certain that changes made in one location do not also need to be made in other places that duplicate the logic they are working on. DRY requires that &amp;quot;Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.&amp;quot; &lt;/p&gt;  &lt;p&gt;&lt;b&gt;Repetition in process calls for automation&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Many processes in software development are repetitive and easily automated. The DRY principle applies in these contexts as well as in the source code of the application. Manual testing is slow, error-prone, and difficult to repeat, so automated test suites should be used, if possible. Integrating software can be time consuming and error-prone if done manually, so a build process should be run as frequently as possible, ideally with every check-in. Wherever painful manual processes exist that can be automated, they should be automated and standardized. The goal is to ensure there is only one way of accomplishing the task, and it is as painless as possible. &lt;/p&gt;  &lt;p&gt;&lt;b&gt;Repetition in logic calls for abstraction&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Repetition in logic can take many forms. Copy-and-paste &lt;i&gt;if-then&lt;/i&gt; or &lt;i&gt;switch-case&lt;/i&gt; logic is among the easiest to detect and correct. Many design patterns have the explicit goal of reducing or eliminating duplication in logic within an application. If an object typically requires several things to happen before it can be used, this can be accomplished with an Abstract Factory or a Factory Method. If an object has many possible variations in its behavior, these behaviors can be injected using the Strategy pattern rather than large &lt;i&gt;if-then&lt;/i&gt; structures. In fact, the formulation of design patterns themselves is an attempt to reduce the duplication of effort required to solve common problems and discuss such solutions. In addition, DRY can be applied to structures, such as database schema, resulting in normalization. &lt;/p&gt;  &lt;p&gt;&lt;b&gt;A Matter of principle&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Other software principles are also related to DRY. The Once and Only Once principle, which applies only to the functional behavior of code, can be thought of as a subset of DRY. The Open/Closed Principle, which states that &amp;quot;software entities should be open for extension, but closed for modification,&amp;quot; only works in practice when DRY is followed. Likewise, the well-known Single Responsibility Principle requires that a class have &amp;quot;only one reason to change,&amp;quot; and relies on DRY. &lt;/p&gt;  &lt;p&gt;When followed with regard to structure, logic, process, and function, the DRY principle provides fundamental guidance to software developers and aids the creation of simpler, more maintainable, higher-quality applications. While there are scenarios where repetition can be necessary to meet performance or other requirements (e.g., data denormalization in a database), it should be used only where it directly addresses an actual rather than an imagined problem. &lt;/p&gt;  &lt;p&gt;(see also: &lt;a href="http://stevesmithblog.com/blog/dry-don-rsquo-t-repeat-yourself-motivator/"&gt;Don’t Repeat Yourself motivational poster&lt;/a&gt;)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/si2FsPWekEI" height="1" width="1"/&gt;</description></item><item><title>Principles, Patterns, and Practices of Mediocre Programming</title><link>http://stevesmithblog.com/blog/principles-patterns-and-practices-of-mediocre-programming/</link><pubDate>Thu, 08 Oct 2009 18:43:49 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/principles-patterns-and-practices-of-mediocre-programming/</guid><dc:creator>ssmith</dc:creator><slash:comments>23</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;p&gt;This is my first pass at a list of anti-principles, anti-patterns, and anti-practices that make up &lt;em&gt;mediocre programming&lt;/em&gt;.&amp;#160; I’m hoping to refine this list and update this listing based on community feedback, so please leave a comment or contact me to let me know what I’ve missed, and I’ll gladly credit you with a name and a link if you’d like.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font size="5"&gt;Principles&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Fast Beats Right (FBR)&lt;/strong&gt; – It’s more important to get something done that probably works, or that works right now even if it will be hard to change later, than to spend time ensuring that it is correct or is well designed.&amp;#160; This is classic “cowboy coder” or “&lt;a href="http://www.joelonsoftware.com/items/2009/09/23.html"&gt;duct tape programmer&lt;/a&gt;” thinking, and sometimes management may be OK with dictating that for a given feature or prototype, speed is more important than anything else.&amp;#160; However, for typical, long-term projects where someone is going to have to maintain the application for years to come, this can be a very expensive principle to follow.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Principle(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;“Continuous attention to technical excellence and good design enhances agility” – &lt;a href="http://agilemanifesto.org/principles.html"&gt;Agile Manifesto&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Feaping Creaturitis Driven (FCD)&lt;/strong&gt;&amp;#160; - A project built using FCD never, ever has a cycle that completes appropriately. Just when you can see the horizon, someone on the team adds yet another nozzle or value that they are desperately in love with. &lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Principle(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Plan with a short delivery schedule, deliver on that plan, and plan the next cycle.&amp;#160; Avoid throwing new work into the current cycle.&lt;/p&gt;    &lt;p&gt;“Deliver working software frequently, from a couple of weeks to a couple of months, with a preference to the shorter timescale.” – &lt;a href="http://agilemanifesto.org/principles.html"&gt;Agile Manifesto&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://www.rogerpence.com"&gt;Roger Pence&lt;/a&gt; (with credit to PL Plauger for the term “feeping creaturitis”)&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Assumption Driven Programming (ADP)&lt;/strong&gt; – Rather than wasting time thinking about edge cases, assume that the user will only use your software the way you mean for them to, and that nothing bad will happen.&amp;#160; This is the quickest way to knock out a feature and be able to demo it to the customer anyway (as long as you are in charge of the demo script), and besides, it should be &lt;em&gt;obvious&lt;/em&gt; to anyone how they’re &lt;em&gt;supposed&lt;/em&gt; to use the application, so it’s the user’s fault if they do something unexpected.&lt;/p&gt;  &lt;p&gt;Some typical “Assumptionist” questions:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;“What do you mean, you can’t change the configuration on a shared host?”&lt;/p&gt;    &lt;p&gt;“Why would the user type garbage in the address bar?”&lt;/p&gt;    &lt;p&gt;“Who would try to put a script file through this image upload form?”&lt;/p&gt;    &lt;p&gt;“Why would someone type SQL into a textbox clearly labeled ‘username’?”&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;ADP often leads to uncaught “sad path” bugs and security holes.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Principle(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Practice some defensive coding, and verify that inputs to your method are what you expect before working with them.&lt;/p&gt;    &lt;p&gt;Define with the customer what should happen when the unexpected occurs, and treat this behavior like any other feature.&lt;/p&gt;    &lt;p&gt;Write one test for the “happy (expected) path” but write several tests for various “sad paths” where things could go wrong.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;    &lt;h6&gt;&lt;font size="2"&gt;&lt;a href="http://profiles.yahoo.com/blog/FFXG7KNZPDPAKTN3Q2O3ISE5BM?eid=rJzZAgIwny4SUQfzTSydYL4GwMd6SAUUTPW9jwIL2FEY5xGkqA"&gt;Felix Pleşoianu&lt;/a&gt;&lt;/font&gt;&lt;/h6&gt;    &lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/020161622X?ie=UTF8&amp;amp;tag=aspalliancecom&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=020161622X"&gt;The Pragmatic Programmer&lt;/a&gt; calls this Programming by Coincidence (per &lt;a href="http://surrealization.com/"&gt;Adam&lt;/a&gt;)&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Telemarketer Principle (TP)&lt;/strong&gt;&amp;#160; - Programming is about control, and the best way to ensure that your software does exactly what you want is for you to manage exactly when and how it behaves at all times.&amp;#160; The Telemarketer Principle is summed up as “Make lots of calls to anything you might need in the system for your program.”&amp;#160; If you cede control to other modules or let the system or a framework do things on your behalf, can you really be sure they’ll do it right?&amp;#160; And just in case, don’t forget that for anything really important you can always &lt;strong&gt;Reinvent The Wheel (RTW)&lt;/strong&gt; and call your own routine all over the place instead.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Preferred Principle(s)&lt;/strong&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Follow the &lt;a href="http://en.wikipedia.org/wiki/Hollywood_principle"&gt;Hollywood Principle&lt;/a&gt;: “Don’t call us; we’ll call you.”&amp;#160; If you find for instance that you’re making Response.Write or Console.Write or Window.Draw calls throughout your business logic, it’s likely that your design would benefit from this principle.&lt;/p&gt;    &lt;p&gt;The &lt;a href="http://en.wikipedia.org/wiki/Dependency_inversion_principle"&gt;Dependency Inversion Principle&lt;/a&gt; can also help remove dependencies like these, and set up the ability for the callee to become the caller.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;(do you have more &lt;em&gt;principles&lt;/em&gt; of mediocre programming?&amp;#160; Leave a comment below!)&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font size="5"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font size="5"&gt;Patterns&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Static Cling Pattern (SCP)&lt;/strong&gt; – Static (global) methods and objects are often an expedient way to add some functionality to an application without spending any time worrying about where the “right” place for it to live might be.&amp;#160; Using static methods for truly stateless operations that work only on the parameters passed in and do not have any other dependencies is fine, but problems arise when static methods instantiate objects or call other methods outside the scope of the passed in parameters.&amp;#160; Once this has gone on for a while, it can be very difficult to introduce seams into the application to decouple its components from one another – it has begun to suffer from “static cling”.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Pattern(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Dependency Inversion Principle.&amp;#160; Calls to static methods are direct dependencies that cannot be easily injected.&amp;#160; Replace them with interfaces and calls to instance methods on injected objects.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;More Reading&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/"&gt;Static Methods are Death to Testability&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;    &lt;h6&gt;&lt;font size="2"&gt;&lt;a href="http://code.google.com/p/google-guice/"&gt;Bob Lee, (of Guice)&lt;/a&gt; - (via &lt;a href="http://kohari.org/"&gt;Nate Kohari&lt;/a&gt;)&lt;/font&gt;&lt;/h6&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Vestigial Structures Pattern (VSP)&lt;/strong&gt; – When features are removed that span multiple tiers, the bits that are no longer used are left behind “just in case.”&amp;#160; As in biology, Vestigial Structures are usually in a degenerate, atrophied, or rudimentary condition, and tend to be much more variable than similar parts. Although structures usually called &amp;quot;vestigial&amp;quot; are largely or entirely functionless, a vestigial structure may retain lesser functions or develop minor new ones.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Pattern(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Vestigial Structures are typically symptoms of a muddled architecture, where it is difficult to tell where one feature ends and another begins.&amp;#160; See also &lt;a href="http://en.wikipedia.org/wiki/Big_ball_of_mud"&gt;Big Ball of Mud&lt;/a&gt;.&lt;/p&gt;    &lt;p&gt;Source control.&amp;#160; Delete it if it’s not used; if you end up needing it, pull it back out of source control.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://richarddingwall.name"&gt;Richard Dingwall&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Flags Over Objects (FOO)&lt;/strong&gt; – Instead of using objects, polymorphism, or delegation, it’s much faster to just add a flag to a class and expose it.&amp;#160; Then anywhere in the system that cares about that condition can simply add an if(foo.IsBar()) { … } clause to deal with this condition.&amp;#160; Who needs inheritance?&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Pattern(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Replace Conditional with Polymorphism, &lt;a href="http://www.amazon.com/gp/product/0201485672?ie=UTF8&amp;amp;tag=aspalliancecom&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0201485672"&gt;Refactoring&lt;/a&gt;, p255 (&lt;a href="http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html"&gt;online&lt;/a&gt;)&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/if-bugs.html"&gt;Curtis Cooley&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;The God Class (TGC)&lt;/strong&gt; – Everybody knows that object-oriented programming is great because it lets us leverage and reuse classes to do all the work in our programs.&amp;#160; And of course, nobody wants to have to look for the &lt;em&gt;right&lt;/em&gt; class to do something, and the more classes you have, the less powerful each one is.&amp;#160; It’s much better to just write (in Lord of the Rings voice here) &lt;strong&gt;one class to rule them all&lt;/strong&gt;, a master class, capable of doing anything and everything your application might require.&amp;#160; And who cares if it’s 5,000 lines of code?&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Pattern(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Follow the &lt;a href="http://en.wikipedia.org/wiki/Single_responsibility_principle"&gt;Single Responsibility Principle&lt;/a&gt; (SRP), which states that a class should have only one reason to change.&amp;#160; If your class has more than one responsibility, it should be split into several classes.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://blog.coreycoogan.com/"&gt;Corey Coogan&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Data Driven Architecture (DDA)&lt;/strong&gt; – Professional developers know that building applications in layers or tiers is a best practice.&amp;#160; The traditional approach is to have at least 3 such layers: UI, Business, and Data.&amp;#160; When organized following the DDA pattern, the Business Layer will reference the Data Layer, and the UI Layer will reference the Business Layer.&amp;#160; This is obvious, since clearly the Business Layer needs to be able to fetch and persist data, so it has to reference the Data Layer, and the UI layer needs to provide an interface to the Business Layer, and so must reference it.&amp;#160; The benefit of this layered approach is that the Data Layer could, theoretically, be completely rewritten without any changes being made to the UI layer.&amp;#160; Only the layer one step up would need to change (and it must change, since any interfaces used can’t belong to the Business Layer using this approach, because then the Data Layer would need to reference the Business Layer in order to implement such interfaces, creating a circular reference.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Pattern(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;The application’s Business Layer should be its Core.&amp;#160; It should not depend on anything if possible, and certainly not on Infrastructure like data access.&amp;#160; All project dependencies should point to the Business Layer / Core, which should define the interfaces it requires its collaborators to implement.&amp;#160; See &lt;a href="http://jeffreypalermo.com/blog/the-onion-architecture-part-1/"&gt;Onion Architecture&lt;/a&gt; and &lt;a href="http://c2.com/cgi/wiki?HexagonalArchitecture"&gt;Hexagonal Architecture&lt;/a&gt;.&lt;/p&gt;    &lt;p&gt;The &lt;a href="http://en.wikipedia.org/wiki/Dependency_inversion_principle"&gt;Dependency Inversion Principle&lt;/a&gt; can be valuable in achieving this reversal of project reference direction. &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;(do you have more &lt;em&gt;patterns&lt;/em&gt; of mediocre programming?&amp;#160; Leave a comment below!)&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font size="5"&gt;Practices&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Found On Internet (FOI) – &lt;/strong&gt;When faced with a problem, the quickest fix is often to search the Internet.&amp;#160; This is a valuable and effective way to research solutions.&amp;#160; The mediocre programmer’s approach to this, however, is to locate the first blog post or forum response that looks like it might be a fit to the problem at hand, and then to cut and paste the code, hack at it until it compiles, and run the application to see if it seems to work.&amp;#160; If it does, move on to the next fire.&amp;#160; If not, continue hacking until it does or go to the next item in the search results.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Practice(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Spike a solution using the found code and see if it works in a variety of scenarios.&amp;#160; &lt;/p&gt;    &lt;p&gt;Write some tests to confirm the behavior of the code does what it claims and works according to your expectations.&amp;#160; &lt;/p&gt;    &lt;p&gt;Review a variety of possible approaches to the problem and try to determine if you’re actually asking the right question before jumping on a possible solution.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Premature Optimization (PO)&lt;/strong&gt; – Developers like to write code that runs as quickly as possible.&amp;#160; Certainly, performance is one factor in nearly all software, but like all factors it must be weighed against other attributes like maintainability and velocity of feature delivery.&amp;#160; The practice of optimizing code before one knows if a bottleneck exists in the current code, or if the actual production performance of the application will not be sufficient without such optimization, tends to result in waste.&amp;#160; More often than not, the code being optimized is either good enough as is, or simply is not the bottleneck and thus any optimizations made to it will not impact the actual performance of the application.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Practice(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Get actual performance requirements from the customer and write tests to ensure that given scenarios comply with these performance requirements.&amp;#160; Do not optimize unless these tests fail.&lt;/p&gt;    &lt;p&gt;Collect actual data on the performance of the application and identify where the bottlenecks are.&amp;#160; Spend your time eliminating the actual bottlenecks identified by your analysis, rather than guessing where the problem might be.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Copy-Paste-Compile (CPC)&lt;/strong&gt; – Sometimes an application exhibits a bug or requires new functionality that has already been corrected elsewhere.&amp;#160; All that needs done is to locate the correct section of code, copy it, and paste it into the section of code that requires this behavior.&amp;#160; Make sure it still compiles and check it in.&amp;#160; Another task complete!&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Practice(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Get actual performance requirements from the customer and write tests to ensure that given scenarios comply with these performance requirements.&amp;#160; Do not optimize unless these tests fail.&lt;/p&gt;    &lt;p&gt;Collect actual data on the performance of the application and identify where the bottlenecks are.&amp;#160; Spend your time eliminating the actual bottlenecks identified by your analysis, rather than guessing where the problem might be.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;(whoa, did someone &lt;em&gt;copy and paste the above and forget to update it?&lt;/em&gt;)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Follow &lt;a href="http://stevesmithblog.com/blog/dry-don-rsquo-t-repeat-yourself-motivator/"&gt;Don’t Repeat Yourself&lt;/a&gt; and move logic that repeats into its own methods or objects.&amp;#160; Fight duplication wherever you find it in your code.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Reinventing The Wheel (RTW)&lt;/strong&gt; – Part of an application requires a little algorithm or utility that no doubt someone else has encountered before, but rather than using an existing framework class or well-known industry solution, the developer takes this as an opportunity to write the best XXX utility ever.&amp;#160; It’s often more fun to write an algorithm or &lt;a href="http://regexlib.com/"&gt;regular expression&lt;/a&gt; than to deliver yet another boring business form or report, but reinventing the wheel doesn’t add value.&amp;#160; (also seen sometimes as &lt;strong&gt;Not Invented Here (NIH)&lt;/strong&gt;).&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Practice(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Know and use the framework.&amp;#160; If you’re a .NET developer, the .NET framework has a ton of functionality in it and is usually a good first place to look.&lt;/p&gt;    &lt;p&gt;Share knowledge within your organization.&amp;#160; It’s likely someone else in your company already has solved this problem.&lt;/p&gt;    &lt;p&gt;Quickly estimate the time it would take to build the functionality from scratch, and the cost of your time.&amp;#160; See if a commercial tool could do the job for less, and see if management is willing to invest in such a tool.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Copy Folder Versioning(CFV)&lt;/strong&gt; – Sometimes, changes to an application are big enough that they seem scary (even to a cowboy coder), so they decide to make a backup.&amp;#160; Of course, there’s no source control software in use, so naturally the way to go is to make a copy of the folder containing the project (and, if you’re being extra professional, rename it from “Copy (2) of FooProject” to something more useful).&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Practice(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Use Source Control!&amp;#160; Please, if you’re still not using some kind of source control, start doing so today!&lt;/p&gt;    &lt;p&gt;Seriously, use source control.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://brendan.enrick.com/"&gt;Brendan Enrick&lt;/a&gt; helped come up with the &lt;a href="http://en.wikipedia.org/wiki/Three-letter_abbreviation"&gt;TLA&lt;/a&gt; for this one.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Golden Hammer (GH)&lt;/strong&gt; – The Golden Hammer refers to a particular language, framework, library, or tool with which you are familiar, and with which you’re certain you can solve any problem.&amp;#160; When wielding the Golden Hammer, every obstacle looks like a nail, and any suggestions by teammates that other tools might be better-suited to the task at hand go unheard. (aka Silver Bullet, Magic Bullet)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Practice(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Learn or at least become familiar with a variety of languages, tools, and frameworks.&amp;#160; Keep an open mind about how to approach problems as you build an application.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Peter Hickman&lt;/p&gt;    &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Shiny Toy (ST)&lt;/strong&gt; – The Shiny Toy is latest, coolest tool or technology available.&amp;#160; Its potential is boundless.&amp;#160; It can solve world hunger and bring about lasting peace in the Middle East.&amp;#160; But it’s still beta so of course you need to spend some time learning it, since there’s no documentation for it yet and nobody you’ve heard of has released a live application running with it.&amp;#160; You’re sure it will do everything the application needs, though, without too much extra effort… (this is related to the Golden Hammer practice, above, but taken to the opposite extreme.&amp;#160; Sometimes you will find someone who thinks their &lt;strong&gt;Shiny Toy&lt;/strong&gt; is a &lt;strong&gt;Golden Hammer&lt;/strong&gt;, though!).&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Preferred Practice(s)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Prefer existing and well-known tools and patterns to unproven ones for production use.&amp;#160; Learn about the new on your own time, at conferences, for your blog, or if you’re sure it solves a real problem the application has, with a &lt;a href="http://c2.com/xp/SpikeSolution.html"&gt;spike solution&lt;/a&gt; that can quickly demonstrate whether or not it will work.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://www.myviewstate.net/"&gt;Kevin Babcock&lt;/a&gt; (renamed “Shiny Toy” by Steve Smith)&lt;/p&gt;    &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;(do you have more &lt;em&gt;practices&lt;/em&gt; of mediocre programming?&amp;#160; Leave a comment below!)&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Additional References&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://systeminetwork.com/article/anti-patterns-avoid-programming-dark-side"&gt;Anti-Patterns : Avoid Programming the Dark Side&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Book: &lt;a href="http://www.amazon.com/gp/product/0131857258?ie=UTF8&amp;amp;tag=aspalliancecom&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0131857258"&gt;Agile Principles, Patterns, and Practices in C#&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Ward Cunningham’s (large) &lt;a href="http://c2.com/cgi/wiki?AntiPatternsCatalog"&gt;Anti-Pattern Catalog&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/BpncFKTdBLI" height="1" width="1"/&gt;</description></item><item><title>Why not Classic (Legacy) ASP?</title><link>http://stevesmithblog.com/blog/why-not-classic-legacy-asp/</link><pubDate>Thu, 08 Oct 2009 14:22:55 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/why-not-classic-legacy-asp/</guid><dc:creator>ssmith</dc:creator><slash:comments>20</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;p&gt;Yesterday I got the following email, which I thought raised some good points that I thought were worth addressing here in my blog in addition to the reply I sent directly.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Hi Steve,      &lt;br /&gt;I've been following your blog (as well as Rob C. and Scott H.) as I look into dipping my toes into MVC. I just read you &amp;quot;&lt;a href="http://stevesmithblog.com/blog/how-i-got-started-in-software-development/"&gt;How I Got Started in Software Development&lt;/a&gt;&amp;quot; and your description of the joys of lightweight ASP development really hit home. So I have a perfect question for you.       &lt;br /&gt;I've been in the MIS/IS/IT/ICT field for, well, as long as it's taken me to collect all those acronyms. I've been doing lightweight webapp development since the late 90s, starting first in CDML, then ColdFusion, then classic ASP.       &lt;br /&gt;For the past few years, I've been developing some first-rate classic ASP webapps using a heavy does of AJAX (jQuery) for the UI interactivity. The problem is, nobody takes these webapps seriously, and I'm constantly taking flack for not moving to a &amp;quot;real&amp;quot; framework. And yet as I look at many of these frameworks, they seem like overkill in all the wrong ways. ASP.NET WebForms, in particular, makes me want to run out of the building screaming.       &lt;br /&gt;And yet, many clearly smart and experienced people like yourself are rallying behind these frameworks. What's up? What am I missing? What's the draw? And please don't spin that old &amp;quot;spaghetti code&amp;quot; yarn; as Rob writes, &amp;quot;Spaghetti is as spaghetti does&amp;quot;.       &lt;br /&gt;Here's my thinking: HTTP is stateless. All you need the framework to do is talk to your database and render clean HTML. That's it. All UI interactivity should be handled on the browser in JavaScript, and all design should be handled in CSS. Classic ASP (with the help of jQuery) makes all this SO EASY. And there is nothing to prevent a good developer from writing clean spaghetti-free code with proper abstraction and separation of concerns.       &lt;br /&gt;I don't want to pretend that I'm a VS programmer working on a stateful desktop app. I want clean and efficient HTML, CSS, and JavaScript rendered to the browser. All these frameworks seem to get in the way of that while providing minimal benefit. So in five bullet points: WHAT AM I MISSING?       &lt;br /&gt;Thanks for taking the time to read this, and hopefully even to answer.       &lt;br /&gt;Sincerely,       &lt;br /&gt;another confused old-school ASP guy,&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Here’s my emailed response:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Hi XXX,&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Read these three books when you get a chance:&lt;/p&gt;    &lt;p&gt;1) Working Effectively with Legacy Code by Michael Feathers&lt;/p&gt;    &lt;p&gt;2) Agile Principles, Patterns, and Practices of Software Development in C# by Robert Martin and Micah Martin&lt;/p&gt;    &lt;p&gt;3) Clean Code by Robert Martin&lt;/p&gt;    &lt;p&gt;Now, to properly answer your question.&amp;#160; The big reason why I won’t wish to return to ASP is the lack of ability to reuse and properly test code.&amp;#160; As applications grow in size and complexity these two things are critical to retaining the ability to make major changes to the site’s design.&amp;#160; Include files are a poor substitute for OO patterns of reuse.&amp;#160; Also ASP uses COM which is a PITA in its own right, lacking xcopy deployment and forcing you into DLL hell that I don’t wish to return to (for instance, you can’t have two versions of a given component on the same machine, requiring separate instances of the OS just to run side-by-side versions of an app).&amp;#160; Plus, talking to databases is something that should be done via an ORM tool if at all possible, and certainly not with lots of hand-written ADO plumbing code.&amp;#160; Perhaps the ORM landscape has changed since I last did ASP 9 years ago but and there are numerous effective options, but if not, that alone will ham-string your productivity.&lt;/p&gt;    &lt;p&gt;If you liked the clean separation that ASP afforded, you should like MVC views which will let you do your jQuery + Services approach easily but will also let your services run in .NET where they can take advantage of ORM tools, better performance, unit testing, and inheritance/polymorphism as well as new language features like LINQ and built-in platform features like caching that ASP lacked.&amp;#160; Give it 3 months’ time on a real project and email me back and let me know what you think.&lt;/p&gt;    &lt;p&gt;Thanks,&lt;/p&gt;    &lt;p&gt;Steve&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;I’d also recommend some of the other books listed on my &lt;a href="http://stevesmithblog.com/blog/favorite-developer-books/"&gt;Favorite Developer Books&lt;/a&gt; post, which I’ll need to update soon to include the two Martin books I referenced in my response.&amp;#160; Yes, you can write well-structured web applications in ASP.&amp;#160; Yes, writing them with jQuery and a clean separation between UI and data and business logic is a good way to avoid spaghetti code.&amp;#160; However, as the reader has found, support for this technology stack is all but gone, and there are many benefits to be had from moving to a modern stack like .NET (which itself is getting somewhat long in tooth, nearing 10 years of age).&amp;#160; If he’s very enamored with the dynamic nature of ASP, Ruby is likely a good choice for him to check out as an alternative.&amp;#160; But if he likes the Microsoft toolset and stack, then I’ll stick with my recommendation that he go to ASP.NET MVC, and avoid the VB6-like ASP.NET Web Forms model.&lt;/p&gt;  &lt;p&gt;Another benefit I neglected to mention in using .NET over ASP is the ability to debug the code.&amp;#160; Honestly I don’t use the debugger that often (tests &amp;gt; debugger usually) but when I do need it it’s extremely valuable.&amp;#160; And I can say that on the rare occasions when I’ve had to work with ASP code in the last 9 years or so, both my own and clients, it’s been universally unpleasant compared to ASP.NET MVC, which has been a joy across the board.&amp;#160; However, I wouldn’t say that any of the ASP apps were written as well as the author of the email writes his.&lt;/p&gt;  &lt;p&gt;How many other readers are still using ASP for new applications today?&amp;#160; Are you trying to incorporate modern advances like ORM tools, jQuery, web services, etc. or do you build them the same way people built them 10 years ago?&amp;#160; What are the biggest pain points you face?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/Ns42pY_gsyU" height="1" width="1"/&gt;</description></item><item><title>SQL Divide By Zero Error Solved</title><link>http://stevesmithblog.com/blog/sql-divide-by-zero-error-solved/</link><pubDate>Mon, 28 Sep 2009 17:07:00 GMT</pubDate><guid isPermaLink="true">http://stevesmithblog.com/blog/sql-divide-by-zero-error-solved/</guid><dc:creator>ssmith</dc:creator><slash:comments>2</slash:comments><category domain="http://stevesmithblog.com/blog/">Blog</category><description>&lt;p&gt;Recently a report that had been running fine for months began failing with a Divide By Zero exception.&amp;#160; This report is a summary of a lot of data and is contained in a stored procedure which uses quite a few table variables to do its job.&amp;#160; Here’s part of it:&lt;/p&gt;  &lt;div&gt;   &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;     &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;declare&lt;/span&gt; @AccountManagerRevenueByFormat &lt;span style="color: #0000ff"&gt;table&lt;/span&gt; &lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;(FormatID &lt;span style="color: #0000ff"&gt;int&lt;/span&gt;, AccountManagerRevenue money)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;insert &lt;span style="color: #0000ff"&gt;into&lt;/span&gt; @AccountManagerRevenueByFormat&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;select&lt;/span&gt; CreativeFormatID, &lt;span style="color: #0000ff"&gt;sum&lt;/span&gt;(AmountEarned)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;from&lt;/span&gt; lq_AccountManagerRevenueDetail amr&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;inner&lt;/span&gt; &lt;span style="color: #0000ff"&gt;join&lt;/span&gt; lq_Placement p &lt;span style="color: #0000ff"&gt;on&lt;/span&gt; amr.PlacementID = p.ID&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;where&lt;/span&gt; DateRecorded &lt;span style="color: #0000ff"&gt;between&lt;/span&gt; @StartDate &lt;span style="color: #0000ff"&gt;and&lt;/span&gt; @EndDate&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;group&lt;/span&gt; &lt;span style="color: #0000ff"&gt;by&lt;/span&gt; CreativeFormatID&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Ultimately, after a bunch of such tables are created, all of the results are pulled out using a final select that joins together each of the table level variables.&amp;#160; One of the columns displayed is a CPM value, or “cost per thousand,” which is how online ad impressions are typically priced (this report was part of &lt;a href="http://lakequincy.com/"&gt;AdSignia, Lake Quincy Media’s ad network management platform&lt;/a&gt;).&amp;#160; In order to calculate CPM, the query included something like this:&lt;/p&gt;

&lt;div&gt;
  &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;
    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;isnull( isnull(Revenue,0) / ( &lt;span style="color: #0000ff"&gt;nullif&lt;/span&gt;(Impressions,0) / 1000),0) [CPM],&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The purpose of the various isnull and nullif functions is to guard against divide by zero exceptions and to ensure that any lack of results is shown as zero.&amp;#160; For instance, the following returns 0 as expected:&lt;/p&gt;

&lt;div&gt;
  &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;
    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;select&lt;/span&gt; isnull( isnull(1,0) / ( &lt;span style="color: #0000ff"&gt;nullif&lt;/span&gt;(0,0) / 1000),0) [CPM]&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;And when impressions are 1000 and revenue is 1, the CPM of 1 is returned as expected:&lt;/p&gt;

&lt;div&gt;
  &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;
    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;select&lt;/span&gt; isnull( isnull(1,0) / ( &lt;span style="color: #0000ff"&gt;nullif&lt;/span&gt;(1000,0) / 1000),0) [CPM]&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;However, despite all of these guards, one thing can still go wrong.&amp;#160; Do you see it?&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;The problem has to do with the fact that Impressions are an integer field, so by default the division is going to drop any remainder.&amp;#160; Thus, the following doesn’t evaluate to slightly more than 1, but rather results in a divide by zero error:&lt;/p&gt;

&lt;div&gt;
  &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;
    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;select&lt;/span&gt; isnull( isnull(1,0) / ( &lt;span style="color: #0000ff"&gt;nullif&lt;/span&gt;(999,0) / 1000),0) [CPM]&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;To counteract this, the Impressions field needs to be converted to a numeric or floating point data type before being divided, like so:&lt;/p&gt;

&lt;div&gt;
  &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;
    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;select&lt;/span&gt; isnull( isnull(1,0) / ( &lt;span style="color: #0000ff"&gt;nullif&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;convert&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;float&lt;/span&gt;,999),0) / 1000),0) [CPM]&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;which results in: 1.001001001001.&lt;/p&gt;

&lt;p&gt;Returning to the original snippet, it looks like this:&lt;/p&gt;

&lt;div&gt;
  &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;
    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;isnull( isnull(Revenue,0) / ( &lt;span style="color: #0000ff"&gt;nullif&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;CONVERT&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;float&lt;/span&gt;,Impressions),0) / 1000),0) [CPM],&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StevenSmith/~4/-bndwiCoQNk" height="1" width="1"/&gt;</description></item></channel></rss>
