You can code it, I can help!

Prairie Development Conference

On Jun 2nd and 3rd I’ll be speaking at the PrairieDevCon 2010 in Regina!!! Prairie_Dev_Con_Presenter You can read about it at http://www.prairiedevcon.com/. It would be two complete days of sessions with four tracks and many cool speakers!!!! I will do two presentations:
Real World Behaviour Driven Development
Behaviour Driven Development drives you process towards keeping the focus on the stakeholder’s goals while discovering new features to achieve those goals. But... what does it mean in a .NET project to use BDD? What do I have to change? What tools are available? Can I use it for project with actual deadlines? How the quality will be improved? We are going to see a real world example from start to finish using BDD and TDD while answering all those questions. After the session you will have the foundation to apply BDD with confidence on any .NET project. Track: Developer Foundation Style: Dojo (Bring your laptop and code with me!)
Test Driven Development Patterns for .NET Developers
Test Driven Development is a methodology that will help us to discover our model while improving the quality of our software. We are going to see different patterns to help us deal with day to day problems like constructor initialization, exception testing, combinatorial tests, database testing, and many others. Track: Developer Foundation Style: Lecture Hope to see you there!!!

Snapping Strategy Part II

New inspiration

One of many great things about blogging is that you get feedback from other developers. My good friend Sebastian (@paraseba) suggested to use a Chain-of-Responsibility instead of a Composite to find the an active snapping strategy. If u have no idea what I'm talking about, please read the previous post about "Implementing a Snapping Strategy".

CoR Pattern

In the Design Patterns book, the authors describe the Chain of Responsibility pattern like this:
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
In our case this means that we should give the opportunity to each strategy to do the snapping and if no snapping is available should call the “chained” strategy to see if the point should be snapped. The solution it's working, good time to refactor (Red, Green, Refactor) to add more flexibility to our strategies. Let’s explore what changes do we need to do in order to be able to chain the strategies.

Chained Strategies

I don’t think we should modify the interface, no one needs to know which pattern are we implemented, so I am going to create a new abstract class called ChainedSnappingStrategy. This class will be the base implementation for all the other strategies with two responsibilities:
  • Call the next strategy in the chain if no snapping was applied
  • If it’s the end of the chain, return the same point.
Our hierarchy updated will look something like this: image I added a new abstract method called SnapImpl to implement in each concrete class the snapping (same as we have now). The Snap method should call the implementation first and then check if the point was snapped or not, if not should go to the next strategy. Here is the code:
   1: public abstract class ChainedSnappingStrategy : ISnappingStrategy
   2: {
   3:     public ISnappingStrategy Next { get; set; }
   5:     public Point Snap(Point point, IDrawingContext context)
   6:     {
   7:         var result = this.SnapImpl(point, context);
   9:         if (this.Next != null && result == point)
  10:         {
  11:             result = this.Next.Snap(point, context);
  12:         }
  14:         return result;
  15:     }
  17:     protected abstract Point SnapImpl(Point point, IDrawingContext context);
  18: }
And voila, our strategies are chained!

Snapping Strategy

The application

For the past eight months I’ve been working on a WPF  desktop application that has a tool to draw. Very similar to most drawing application, you select the Drawing tool in the ribbon/toolbox and then you are able to draw on the canvas until you close the shape or cancel the tool.

The new requirement

Now, we need to draw using snapping. Snapping means that when you are drawing close to a particular point (considering a distance) the mouse will move but the drawing line not, will stay snapped to the point. In this case we need to consider three cases:
  1. Snapping on the X axis (horizontal axis)
  2. Snapping on the Y axis (vertical axis)
  3. Snapping when the line has an 90 degree angle with the previous line

The solution

Where should I add the snapping?

So I started by writing a test  (Yes, full TDD) that checks when I’m close to the X axis (3 pixels or less) then the drawing tool should snap. My test failed gloriously and I was ready to start my implementation in order to make the test pass… now… the question is… who is responsible for snapping?

The Drawing Tool

Let’s review the drawing tool interaction to understand how it works using MVVM.
  • The Canvas receives a mouse move event and passes that information to the DrawingTool
  • The DrawingTool raises a LineMoved event
  • The view model receives the event and notifies the view
  • The view shows the line ending on the new point
A sequence diagram should look something like this: image

Who is responsible for snapping?

The DrawingTool does not know about snapping, but it should, because is raising the event… However considering the SRP (single responsibility principle) makes more sense to delegate this decision to another class, behold the ISnappingStrategy. I decided to inject the strategy into the DrawingTool constructor (via IoC) and the DrawingTool will query the strategy for each point before raising the event, thus the event will contain the point decided by the strategy. Let’s look at the new sequence diagram with the strategy added to the interaction:

Why a strategy? Is that the GOF pattern?

Indeed it is, why? Because I wanted to encapsulate the algorithm I’m going to use to snap, snapping to X axis has different rules than snapping to the previous line when having a 90 degree angle. Please refer to the GOF book “Design Patterns” or Google for more examples and diagrams.

Snapping Strategy Hierarchy

I have so far one interface ISnappingStrategy and three concrete strategies that implement the interface:
Each strategy when called will return the snapped point or, if no snapping is required the same point. So far, so good…. now I have implemented them, and each one passes the unit tests. However I don’t want to use just one of the them, I want to use all of them combined. I’d like to use first SnapToX and if does not snap, then SnapToY, etc, etc.

Enters the Composite Strategy

The Composite pattern is another GOF pattern and the goal is to treat a group of objects like they where a single instance. In this case, I want to use multiple snapping strategies like they were just one strategy. Here is the idea, let’s create the Composite with a collection of strategies, and when called the Composite will iterate thru them until it finds a new snapping point or, if no snapping should happen, return the same point. So, with the Composite, the new hierarchy looks like: image And the code for the composite snapping would be:
   1: public Point Snap(Point point, IDrawingContext context)
   2: {
   3:     var found = this._strategies.Find(s => s.Snap(point, context) != point);
   5:     return found == null ? point : found.Snap(point, context);
   6: }

The cherry on top

Now we need to configure all this, luckily we can use Binsor to configure our Windsor container and it will look something like this:
   1: component "SnapToX", ISnappingStrategy, VectorSnappingStrategy:
   2:   x = 1
   3:   y = 0
   5: component "SnapToY", ISnappingStrategy, VectorSnappingStrategy:
   6:   x = 0
   7:   y = 1
   9: component "SnapToPrevious", ISnappingStrategy, PreviousLineSnappingStrategy
  11: component "SnappingStrategy", ISnappingStrategy, CompositeSnappingStrategy:
  12:   strategies = [@SnapToX, @SnapToY, @SnapToPrevious]
  14: component "DrawTool", IDrawTool, DrawTool:
  15:   snapping = @SnappingStrategy
Neat eh?