Wednesday, August 21, 2013

Dhall, Decorator, and Developers Hating Developers

I saw Chander Dhall of Ria Consulting speak at Austin Code Camp this weekend. The talk was titled "10 things every developer must know." I suppose I could give an ordered list of ten items in this blog postings, but I think I shall instead offer an unordered list of things I found interesting in the talk:

  • Chander stressed that developers hate each other. We do not get along and we do not want to have conversations with each other. We just want to hide beneath our headphones and stare into our monitors, or we see ourselves individually as rock stars as opposed to ourselves collectively as teammates. Team dynamics was stressed as the eleventh of Chander's ten items. Projects fail when team members do not get along. For example: The very first of Chander's list was on algorithms and he suggested that developers here especially should not try to come up with solutions in a vacuum. The mechanics of how to tackle algorithms should always be talked out by a team and not delegated to one individual. A complicated puzzle demands a discussion not a hero.
  • Shocker: For quick projects that have a scope of less than two months of time, a team may write code without tests that nonetheless is not buggy by following the principals in the book "Secure Code." Projects that do have longer lifetimes do demand testing.
  • Another shocker: Before you allow a user to do something big, force them to reauthenticate! This was encouraged as a security practice. CAPTCHA helps too and should be used in tandem with reauthentication but must not be seen as bulletproof.
  • A third shocker: Never write integration tests against an actual database. Use an in memory database if need be.
  • Do not let unit testing geek out beyond 85% coverage. 75% to 85% coverage is a good benchmark. When you start writing tests for third party libraries, something is wrong. Testing just the API integration points is sufficient and herein the unit tests also double as documentation.
  • Developers should know more than two languages.
  • Sanity check on all sides, both C# and JavaScript.
  • There are parts of the world where https security is just not supported.
  • The server should not know specifics about the client and the client should not know specifics about the server. Communication between the two should be stateless! Think of scalability.
  • Types of repositories: TFS is a centralized suite of tools, SVN is also centralized, and GIT is distributed.
  • Do not use ThoughtWorks' Go for continuous integration, but instead try: Visual Studio Team System, CruiseControl, TeamCity, Jenkins, or even SnappHQ.com which is currently free and allows one to deploy from development to production with one click.
  • Write .dlls as though they are not changeable.
  • Partial classes and extension methods are hacks not computer science. The decorator pattern is a better way to go when extending code...

Chander had some slides which showed off an implementation of the decorator pattern he made. Based upon these slides I made a one page web forms app that implemented a comparable pattern. The application puts three prices (double values) to labels.

  1. the price of a laptop
  2. the price of a laptop with a mouse
  3. the price of a laptop with both a mouse and a second monitor

 
 

In this example, laptop represents a product one might buy online and the mouse and second monitor represent accoutrements for the laptop. Mice and monitors aren’t really the best example for this. I think Chander used a microphone as one of the appended extra features in his example, but, whatever, let’s pretend for this example that one would only buy a mouse or a second monitor as an extension of a laptop. My code looks like this:

using System;
using Decorate.Core;
using Decorate.Core.AddOns;
namespace Decorate.UserInterface
{
   public partial class Default : System.Web.UI.Page
   {
      protected void Page_Load(object sender, EventArgs e)
      {
         Laptop laptop = new Laptop();
         Label1.Text = laptop.Price.ToString();
         
         Mouse laptopWithMouse = new Mouse(laptop);
         Label2.Text = laptopWithMouse.Price.ToString();
         
         Monitor laptopWithMouseAndSecondMonitor = new Monitor(laptopWithMouse);
         Label3.Text = laptopWithMouseAndSecondMonitor.Price.ToString();
      }
   }
}

 
 

Alright, in order to make the price grow progressively we are handing the laptop into the first additional item and then handing the first additional item into the second additional item. The laptop and the two additional items all have a getsetter for Price which is growing in value progressively. How is this magic possible? The answer: Mouse and Monitor are grandchildren of Laptop which holds the Price getsetter. A middleman between Laptop and a grandchild of Laptop empowers the ability to progressively boost Price.

  1. Label1 ends up with 699.99 in it.
  2. Label2 ends up with 713.54 in it.
  3. Label3 ends up with 863.53 in it.

 
 

The laptop itself is rather plain Jane. There is a virtual field.

namespace Decorate.Core
{
   public class Laptop
   {
      public virtual double Price { get; set; }
      
      public Laptop()
      {
         Price = 699.99;
      }
   }
}

 
 

The middleman inheriting from Laptop while also holding a getsetter for another Laptop is pretty amazing. Can you see how it does its trick?

namespace Decorate.Core
{
   public abstract class AddOnDecorator : Laptop
   {
      protected Laptop _laptop;
      
      protected AddOnDecorator(Laptop laptop)
      {
         _laptop = laptop;
      }
      
      public override double Price
      {
         get
         {
            return base.Price + _laptop.Price;
         }
         set
         {
            base.Price = value;
         }
      }
   }
}

 
 

The children of the middleman are:

  • namespace Decorate.Core.AddOns
    {
       public class Mouse : AddOnDecorator
       {
          public Mouse(Laptop laptop) : base(laptop)
          {
             base.Price = 13.55;
          }
       }
    }
     
  • namespace Decorate.Core.AddOns
    {
       public class Monitor : AddOnDecorator
       {
          public Monitor(Laptop laptop) : base(laptop)
          {
             base.Price = 149.99;
          }
       }
    }

 
 

Here are all of the players coming together in action one more time:

using Decorate.Core;
using Decorate.Core.AddOns;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Decorate.Tests
{
   [TestClass]
   public class Tests
   {
      double laptopPrice = 699.99;
      double mousePrice = 13.55;
      double monitorPrice = 149.99;
      
      [TestMethod]
      public void adding_mouse_then_monitor_to_laptop_behaves_as_expected()
      {
         Laptop laptop = new Laptop();
         Assert.AreEqual(laptop.Price, laptopPrice);
         Mouse laptopWithMouse = new Mouse(laptop);
         Assert.AreEqual(laptopWithMouse.Price, laptopPrice + mousePrice);
         Monitor laptopWithMouseAndSecondMonitor = new Monitor(laptopWithMouse);
         Assert.AreEqual(laptopWithMouseAndSecondMonitor.Price, laptopPrice +
               mousePrice + monitorPrice);
      }
      
      [TestMethod]
      public void adding_monitor_then_mouse_to_laptop_behaves_as_expected()
      {
         Laptop laptop = new Laptop();
         Assert.AreEqual(laptop.Price, laptopPrice);
         Monitor laptopWithSecondMonitor = new Monitor(laptop);
         Assert.AreEqual(laptopWithSecondMonitor.Price, laptopPrice + monitorPrice);
         Mouse laptopWithSecondMonitorAndMouse = new
               Mouse(laptopWithSecondMonitor);
         Assert.AreEqual(laptopWithSecondMonitorAndMouse.Price, laptopPrice +
               monitorPrice + mousePrice);
      }
   }
}

 
 

Wait! I have more to say. I wrote the code above the day before this paragraph and in the time between I have had some time to chew on it and I see two things that could be better:

  1. The grandparental Laptop class may be subclassed by other product classes, allowing for many products to share the same potential for having the add-ons. I tested my theory by making a Dell child of Laptop. It behaves as I expected in code. I think an implementation like this is gravitating us closer to the code Chander showed off too.
    namespace Decorate.Core.Laptops
    {
       public class Dell : Laptop
       {
          public Dell()
          {
             Price = 599.99;
          }
       }
    }
     
  2. My mouse and monitor example is really terrible. Wireless cards and USB ports might be better examples of decorations for a decoration pattern example which should probably be exclusively of composition not aggregation. Chander stressed in his talk to understand the difference between aggregation and composition. In aggregation an item has a collection of things which could exist independent of the item (a cat has n kittens) while in composition lies a deeper dependency wherein there is no parental independence (a cat has n colors).

No comments:

Post a Comment