A Note on Password Security for Mesh Sites

This is based off a somewhat dated blog post from Jeff Atwood over at Coding Horror.

He makes the great point that as web developers, by exposing users to certain things, we have the potential to make them comfortable with things that should raise alarms. One of these is to ask for another site’s credentials.

This is incredibly bad!

And yet.. if you’re on my actual web site (not the RSS feed), check out the little green box there on the left. See that link? “Google login”? That provides a way to log into my site (or parts of it) as a Google account. I don’t do anything crazy with it, in fact it just pops up a window to their login gateway, but the certificate is not prevalent in the browser window, and really, I could have done something similar and captured the password for a replay attack if I was unscrupulous.

As developers, we should be refusing to use non-integrated, remote login API’s. I say non-integrated, because safer methods like OpenID are in fact remote login API’s (distributed credential repositories).

Even if the quick & dirty API’s to pass credentials along aren’t phased by a lack of developers using them, if it’s a rarity to ask for another site’s password, it should be jarring to the user.

A user should not feel comfortable entering another site’s credentials in your site.

Posted on 5/15/2009 7:15:26 AM by Jason Nadal

Permalink | Comments |

Categories:

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

A Foray into WPF : Conditional Data Binding with Graphics

I’ve decided to take a break from some of the more back-end concepts in my game refactoring for a bit, and concentrate for a bit on the front-end. As someone whose primary job function is to release an ASP.NET web application, writing code for Windows is both a significant challenge, as well as a breath of fresh air.

As Andrew Hunt puts it (paraphrased) in Pragmatic Programmer, you should learn outside your common comfort zone – he had stated it in the context of learning languages, but the same holds true for frameworks, and GUIs as well. Or even areas of application development … or even to systems deployment or administration. Each of these realms gives a unique perspective on how applications work in the wild.

But I digress.

To restate the application, it’s a game, whose primary surface is a game Grid ( a two dimensional representation of tiles). The tiles on the Grid can be one of N options, each represented by a different graphical tile. By writing a new WPF UI for the application, I needed to find out how to render the grid.

First things first … drop the Grid onto the XAML form and run, check. Row and Column definitions matching the dimensions of the game Grid object, check. Render a graphic for each cell, check (they’re all the same at this point). Conditionally bind a different color based on the content of the bound data…. uh? Here’s the problem. Upon research, I came across this great interface, called IValueConverter. This interface allows you to map one of your values to another value.

public class CellValueToColorConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    var cell = value as Cell;

    return CellValueToColorString(cell);
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

So that allows us to do a one-way conversion from a cell value to a color (take it for granted there’s another method in there that takes cell, and returns a string color based on cell.CellValue.

So how do we actually do the conditional work in XAML then, you may ask? Well, let’s jump right to some XAML and deconstruct a working example. Keep in mind the Ellipse objects representing each cell are created when the game’s Grid is redrawn.

<Window x:Class="SameBloxUIWinWpf.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SameBloxUIWinWpf.Assets"
    Title="SameBlox" Height="400" Width="400">
    <Window.Resources>
        <local:CellValueToColorConverter x:Key="CellValueConverter"/>
        <Style x:Key="lifeStyle" TargetType="{x:Type Ellipse}">
            <Setter Property="Opacity" Value="0.75" />
            <Setter Property="Fill" Value="{Binding Path=Cell, Converter={StaticResource CellValueConverter}}" />              
        </Style>
    </Window.Resources>
    <DockPanel>
        <StackPanel DockPanel.Dock="Bottom"
                 Orientation="Horizontal"
                 HorizontalAlignment="Center" >
            <Label Height="33" Name="PotentialScore" Width="120" HorizontalAlignment="Left">0</Label>
            <Button x:Name="newGameButton" Margin="5" Click="newGameButton_Click">New Game</Button>            
            <Label Height="33" Name="Score" Width="120" HorizontalAlignment="Right" HorizontalContentAlignment="Right">0</Label>
        </StackPanel>        
        <Grid x:Name="GameGridControl" Background="Black" />
        
    </DockPanel>
</Window>

So above you can see the first interesting part in here is the declaration of a local resource object. CellValueToColorConverter (the class we created before) is given a name: CellValueConverter. Second, a style called “lifeStyle” is created, targetting our ellipses. From here, we’re setting the Fill property (color) to bind the Cell property of the Ellipse’s DataContext to the CellValueConverter! Now because we’re adding the Ellipses dynamically, there’s one more important code snippet, below.

ellipse.Style = Resources["lifeStyle"] as Style;

And that’s really all there is to it.I should mention there’s one other ‘trick’ I added for ease of calculation (& to help perfomance a bit) was to store both the Cell object and the matching coordinate in the DataContext. Below you can see that Cell is one of the properties of that DataContext.

public class WinGridCellDataContext
{
    public GridCoordinate Location { get; set; }
    public Cell Cell { get; set; }
    
}

Posted on 5/13/2009 7:11:32 PM by Jason Nadal

Permalink | Comments |

Categories:

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

BC Article on Student “Hacker”

While the initial allegations of this article are pretty sad (involving a student allegedly sending a potentially libelous email to a campus email distro list), the trail of evidence stated to lead authorities to their suspect is weak at best, and ludicrous at worst.

Some gems from the story:

  • “CS Major who is considered a master of the trade” (yet couldn’t mask a sent email? This is trivial for anyone who understands email)
  • “Two different operating systems to hide his illegal activities … the regular B.C. operating system and the other is a black screen with white font which he uses prompt commands on”. Wow. The college has their own OS? And a command prompt is used to hide?

It seems like after his stuff was seized, many pirated movies, software were found (there’s more incrimination in the article). From the looks of it, they were just looking for a reason to target this student – but this looks like a pretty weak case.

Posted on 5/13/2009 8:12:06 AM by Jason Nadal

Permalink | Comments |

Categories:

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Case Studies of Bad UI are Everywhere

I went out to dinner with friends last night, and got treated to a particularly bad example of UI design gone wild. Somehow this UI evolved over time.

 

So as you can see here, there’s a disaster of light switches. Some for fans and vents, others for lights. The best part is that there’s

  • another row of lights i couldn’t fit into this picture
  • actually another entire ‘panel’ in a different part of the restaurant! (smaller, though)

When looking at this from my perspective, the meaning is lost. The first thing that comes to mind is that there must be some better way to do this (an actual panel, perhaps?), second is that things must be rough when you just want to turn things on or off for one light.

We had to ask what the deal with the lights actually was – apparently it’s really rough to train people to close down at night, because some of the switches stay on, and some stay off.

The punch line of this is that they recently repainted. Apparently they’re half mislabeled now, as they didn’t spend too much effort worrying about putting the right cover plates back on the right switches.

How many times have you seen this in web UI design? This is a real world example of what a form with controls thrown everywhere would look like. It seems ridiculous when we see it on a wall, and it should seem just as ridiculous when we make a webform that just throws 23+ controls at you and says “have fun”.

So what could be done to actually fix this design? Here’s some potential solutions:

  • Group like-switches together. Perhaps “bar lights”, “main seating lights”, “main seating fans” etc.
  • Group switches by context together. Typically we turn this set of lights on at the same time – group those.
  • “Complex” switch – a master programmable control panel (this seems expensive, and a design cop-out to me)
  • Hide them. This is the interesting one. Right now, this is exposed to all of the customers, even those who don’t need to be made aware of the complexity. I wonder what brand psychologists would say about the subtle hints given off by having this in a dining room – it certainly wouldn’t make you feel like a tight ship was being run.If the complexity is a must (again, that should be a last resort), then only show it to those who must interact with it. Perhaps the closer touches the master panel, and the average wait staff only works with the “simple” panel.

One last point – if you see that much mess out front, imagine what’s going on behind the scenes. Behind every light switch should be a box that encases the electrical wiring behind the drywall – this is usually slightly larger than the switch itself. Imagine where all the boxes would fit in the above picture – there’s not much room for the wall at that point… and there must be just a mess of wiring in the wall.

Bad UI can expose your code smell to the client. This is the worst – it’s bad enough if your code is riddled with flaws and is an unmaintainable mess, but the client should not have to have that made visible to them!

Posted on 5/9/2009 9:29:00 AM by Jason Nadal

Permalink | Comments |

Categories: ui | design

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

VMWare Machines : How to Make the Hard Disk Bigger

Here’s an easy commandline way to make the size of a virtual disk (virtual hard disk) larger.

Article

Note that this still requires getting the additional space added to the partition in Windows – which will require a repartitioning tool.

Fortunately, Vista's disk manager allows you to extend existing partitions into the unpartitioned space. All you have to do is click on the drive and hit extend.

Posted on 5/7/2009 7:53:00 PM by Jason Nadal

Permalink | Comments |

Categories: hardware | system | vmware

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Upgrade Vista Enterprise to 7 RC

I attempted to update my home development virtual machine running in VMWare workstation to Windows 7 this evening. I cloned it first – don’t want to get stuck with a no-upgrade path from Windows 7 RC to Windows 7 RTM when it comes out!

I ran across this unfortunate message:

Vista Enterprise cannot be upgraded to Windows 7 Ultimate

This was not a pleasant discovery – figured I was dead in the water unless I upgraded needlessly to Ultimate. When I found out how to upgrade from Enterprise to Ultimate, I realized I can just avoid ultimate altogether and change from Enterprise to Business (sort of a side-grade), and from there to Windows 7 Ultimate.

Here’s the appropriate registry changes:

  • Go to, Start, Run: and type: regedit.exe
  • Go to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
  • Change the key : ProductName from "Windows Vista ™ Enterprise” to “Windows Vista ™ Business”
  • Change the key: EditionID from "Enterprise" to “Business
  • Do not restart (not sure what this will do, but the original instructions made sure to mention this!

Posted on 5/7/2009 7:39:01 PM by Jason Nadal

Permalink | Comments |

Categories:

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

An Exercise in Refactoring: Part 7

This post is a continuation of our previous article, a response to this smelly code.

When last we left off, a majority of our duplicate code was refactored away to the GetKeyFor method. Here’s where our method currently stands.

private int MarkConnectingCellsRecursive(IGrid grid, GridCoordinate location, CellValues clickedValue)
{
    //set the current location to be clicked.
    grid.Columns[location.Column][location.Row].CellState = CellStates.Down;

    //we seed the count at one here, because at least the current cell is to be
    //marked.
    int countOfMarkedCells = 1; 

    //look all around our current cell for other cells of the same value.
    string rightKey = GetKeyFor(grid, location.MoveRight(), clickedValue);
    string leftKey = GetKeyFor(grid, location.MoveLeft(), clickedValue);
    string downKey = GetKeyFor(grid, location.MoveDown(), clickedValue);
    string upKey = GetKeyFor(grid, location.MoveUp(), clickedValue);

    //if the keys ARE the same value, look around them for the same value, mark them, and add them
    //to the count
    countOfMarkedCells += IfContainsClickedValueThenMark(rightKey, grid, location.MoveRight(), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(rightKey, grid, location.MoveLeft(), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(rightKey, grid, location.MoveDown(), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(rightKey, grid, location.MoveUp(), clickedValue);

    return countOfMarkedCells;
}

Note there are essentially two blocks of logic repeated four times.  We can reorganize the lines of code, and then the method extraction should be obvious:

private int MarkConnectingCellsRecursive6(IGrid grid, GridCoordinate location, CellValues clickedValue)
{
    //set the current location to be clicked.
    grid.GetCell(location).CellState = CellStates.Down;

    //we seed the count at one here, because at least the current cell is to be
    //marked.
    int countOfMarkedCells = 1; 

    //look all around our current cell for other cells of the same value.
    string rightKey = GetKeyFor(grid, location.MoveRight(), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(rightKey, grid, location.MoveRight(), clickedValue);
    string leftKey = GetKeyFor(grid, location.MoveLeft(), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(leftKey, grid, location.MoveLeft(), clickedValue);
    string downKey = GetKeyFor(grid, location.MoveDown(), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(downKey, grid, location.MoveDown(), clickedValue);
    string upKey = GetKeyFor(grid, location.MoveUp(), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(upKey, grid, location.MoveUp(), clickedValue);

    return countOfMarkedCells;
}

See the repetition? Pull it out into a method, and you have the code below. This is a VAST improvement over where we started out at the beginning of this series.

private int MarkAndCountCellsWithValueFrom(IGrid grid, CellValues clickedValue, GridCoordinate potentialLocation)
{
    string key = GetKeyFor(grid, potentialLocation, clickedValue);
    return IfContainsClickedValueThenMark(key, grid, potentialLocation, clickedValue);
}

private int MarkConnectingCellsRecursive(IGrid grid, GridCoordinate location, CellValues clickedValue)
{
    //set the current location to be clicked.
    grid.GetCell(location).CellState = CellStates.Down;
    
    int countOfMarkedCells = 1;
    
    //look all around our current cell for other cells of the same value.
    countOfMarkedCells += MarkAndCountCellsWithValueFrom(grid, clickedValue, location.MoveRight());
    countOfMarkedCells += MarkAndCountCellsWithValueFrom(grid, clickedValue, location.MoveLeft());
    countOfMarkedCells += MarkAndCountCellsWithValueFrom(grid, clickedValue, location.MoveUp());
    countOfMarkedCells += MarkAndCountCellsWithValueFrom(grid, clickedValue, location.MoveDown());

    return countOfMarkedCells;
}

There’s still an issue with this code – is it obvious? Look at the name of the method it’s calling. “MarkAndCountCells….:” This violates a fundamental principle methods should do one thing, and do it well – be a Unit of code. The method may be doing two things well, but is still doing two things. More on this to come.

Posted on 5/7/2009 7:25:43 PM by Jason Nadal

Permalink | Comments |

Categories:

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

An Exercise in Refactoring: Part 6

This post is a continuation of our previous article, a response to this smelly code.

When we left off yesterday, I mentioned that the rest of the day’s work on this refactoring (I do have a day job as well, please keep in mind!) I’d spend replacing the Grid.Columns[column][row]:Cell. Well in order to do that, I’ve introduced a new method in the IGrid interface:

public Cell GetCell(GridCoordinate coordinate)
{
    return Columns[coordinate.Column]
        [coordinate.Row];
}

This allows us to do some interesting things in the long term. But really it just makes parameter lists that are passed into methods cleaner.

Now we can get to the fun part – all those GetXKey methods… one for each direction. There’s so much duplication there! See if you can find the similarities in purpose and form between two of the four methods.

private static string GetRightKey(IGrid grid, GridCoordinate location, CellValues clickedValue)
{

    string rightKey = String.Empty;
    bool columnIsNotLastColumn = location.Column < grid.Width - 1; ;
    if (columnIsNotLastColumn
        && grid.Columns[location.Column + 1][location.Row].CellValue == clickedValue
        && IsCellInMarkableState(grid.Columns[location.Column + 1][location.Row]))
    {
        rightKey = location.MoveRight().CreateCellKey();
    }
    return rightKey;
}

private static string GetLeftKey(IGrid grid, GridCoordinate location, CellValues clickedValue)
{
    string leftKey = String.Empty;
    bool columnIsNotFirstColumn = location.Column > 0;
    if (columnIsNotFirstColumn
        && grid.Columns[location.Column - 1][location.Row].CellValue == clickedValue
        && IsCellInMarkableState(grid.Columns[location.Column - 1][location.Row]))
    {
        leftKey = location.MoveLeft().CreateCellKey();
    }
    return leftKey;
}

Any ideas? The problem is that there’s a lot of different code there. They’re looking at different criteria in that first boolean expression, but that logic is clear – is the cell (location) that’s being passed in outside the bounds of the Grid. If the cells a legitimate location, and meets the other criteria, then create the cell key.

So what we can introduce is a GridCoordinateBoundary class, representing the measurable borders (boundaries) of our Grid object.

public class GridCoordinateBoundary
{
    public int MinimumRow { get; set; }
    public int MaximumRow { get; set; }
    public int MinimumColumn { get; set; }
    public int MaximumColumn { get; set; }

    private GridCoordinateBoundary(int minimumRow, int maximumRow, int minimumColumn, int maximumColumn)
    {
        MinimumRow = minimumRow;
        MaximumRow = maximumRow;
        MinimumColumn = minimumColumn;
        MaximumColumn = maximumColumn;
    }

    public static GridCoordinateBoundary CreateFrom(int minimumRow, int maximumRow, int minimumColumn, int maximumColumn)
    {
        return new GridCoordinateBoundary(minimumRow, maximumRow, minimumColumn, maximumColumn);
    }
    
    public bool IsWithinBoundary(GridCoordinate location)
    {
        return (location.Row >= MinimumRow)
               && (location.Row < MaximumRow)
               && (location.Column >= MinimumColumn)
               && (location.Column < MaximumColumn);
    }
}

Now we can start pulling the logic together. We don’t need the 4 unique GetDownKey, GetUpKey, etc.

private static string GetKeyFor1(IGrid grid, GridCoordinate nextLocation, CellValues clickedValue)
{
    string key = String.Empty;
    if (grid.GetBoundary().
            IsWithinBoundary(nextLocation)
        && grid.GetCell(nextLocation).CellValue 
            == clickedValue
        && IsCellInMarkableState(
            grid.GetCell(nextLocation)))
    {
        key = nextLocation.CreateCellKey();
    }
    return key;
}

Note that the logic is right in the if block. Based on literature I’m currently reading (I’d highly recommend Robert C. Martin’s Clean Code so far), pulling the boolean expressions out for variable names is cleaner. This gives us the following result at the end of a short day’s work. I will spend the rest of the day getting the GetCell() method propagated to replace the Column[][] syntax that’s pervasive in the solution.

private static string GetKeyFor(IGrid grid, GridCoordinate nextLocation, CellValues clickedValue)
{
    string key = String.Empty;
    bool locationIsWithinGridBoundary = grid.GetBoundary().IsWithinBoundary(nextLocation);
    bool cellValueIsSameAsClicked = grid.GetCell(nextLocation).CellValue == clickedValue;

    if (locationIsWithinGridBoundary
        && cellValueIsSameAsClicked
        && IsCellInMarkableState(grid.GetCell(nextLocation)))
    {
        key = nextLocation.CreateCellKey();
    }
    return key;
}

Posted on 5/6/2009 7:00:35 AM by Jason Nadal

Permalink | Comments |

Categories: development | codeQuality | refactoring

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

An Exercise in Refactoring: Part 5

This post is a continuation of our previous article, a response to this smelly code.

Remember those columns and rows everywhere, and how tough it was to swap them everywhere? Well this is a prime time to fix that. How else can we reduce parameter count, while still passing in the same information? Enter complex object.

In this case, we can gain:

  • Reduction of column / row parameters to just location
  • Give the CreateCellKey(x,y) method a home (really, it gives a key for a particular coordinate.

So our first step is to create the new object and move in the CreateCellKey method; note this is also implementing a static create method:

public class GridCoordinate
{
    public int Column { get; set;}
    public int Row { get; set; }

    public static string CreateCellKey(GridCoordinate location)
    {
        return location.Column + "," + location.Row;
    }

    public static GridCoordinate Create(int column, int row)
    {
        return new GridCoordinate {Column = column, Row = row};
    }
}

And then we have to fix the calls that just deal with columns and rows. Note the code is longer for now, but we’ll address this with reusability in the near future!

Our resulting changed code is as follows:

private static string GetRightKey(IGrid grid, GridCoordinate location, CellValues clickedValue)
{

    string rightKey = String.Empty;
    bool columnIsNotLastColumn = location.Column < grid.Width - 1; ;
    if (columnIsNotLastColumn
        && grid.Columns[location.Column + 1][location.Row].CellValue == clickedValue
        && IsCellInMarkableState(grid.Columns[location.Column + 1][location.Row]))
    {
        rightKey = GridCoordinate.CreateCellKey(GridCoordinate.Create(location.Column + 1, location.Row));
    }
    return rightKey;
}

private static string GetLeftKey(IGrid grid, GridCoordinate location, CellValues clickedValue)
{
    string leftKey = String.Empty;
    bool columnIsNotFirstColumn = location.Column > 0;
    if (columnIsNotFirstColumn
        && grid.Columns[location.Column - 1][location.Row].CellValue == clickedValue
        && IsCellInMarkableState(grid.Columns[location.Column - 1][location.Row]))
    {
        leftKey = GridCoordinate.CreateCellKey(GridCoordinate.Create(location.Column - 1, location.Row));
    }
    return leftKey;
}

private static string GetDownKey(IGrid grid, GridCoordinate location, CellValues clickedValue)
{
    string downKey = String.Empty;
    bool rowIsNotLastRow = location.Row < grid.Height - 1;
    if (rowIsNotLastRow
        && grid.Columns[location.Column][location.Row + 1].CellValue == clickedValue
        && IsCellInMarkableState(grid.Columns[location.Column][location.Row + 1]))
    {
        downKey = GridCoordinate.CreateCellKey(GridCoordinate.Create(location.Column, location.Row + 1));
    }
    return downKey;
}

private static string GetUpKey(IGrid grid, GridCoordinate location, CellValues clickedValue)
{
    string upKey = String.Empty;
    bool rowIsNotFirstRow = location.Row > 0;
    if (rowIsNotFirstRow
        && grid.Columns[location.Column][location.Row- 1].CellValue == clickedValue
        && IsCellInMarkableState(grid.Columns[location.Column][location.Row - 1]))
    {
        upKey = GridCoordinate.CreateCellKey(GridCoordinate.Create(location.Column, location.Row - 1));
    }
    return upKey;
}

private int IfContainsClickedValueThenMark(string key, IGrid grid, GridCoordinate location, CellValues clickedValue)
{
    int countOfMarkedCells = 0;
    if (key != String.Empty && !_searchedCells.Contains(key))
    {
        _searchedCells.Add(key, String.Empty);
        countOfMarkedCells += MarkConnectingCellsRecursive(grid, location, clickedValue);
    }
    return countOfMarkedCells;
}

private int MarkConnectingCellsRecursive(IGrid grid, GridCoordinate location, CellValues clickedValue)
{
    //set the current location to be clicked.
    grid.Columns[location.Column][location.Row].CellState = CellStates.Down;

    //we seed the count at one here, because at least the current cell is to be
    //marked.
    int countOfMarkedCells = 1; 

    //look all around our current cell for other cells of the same value.
    string rightKey = GetRightKey(grid, location, clickedValue);
    string leftKey = GetLeftKey(grid, location, clickedValue);
    string downKey = GetDownKey(grid, location, clickedValue);
    string upKey = GetUpKey(grid, location, clickedValue);

    //if the keys ARE the same value, look around them for the same value, mark them, and add them
    //to the count
    countOfMarkedCells += IfContainsClickedValueThenMark(rightKey, grid, GridCoordinate.Create(location.Column + 1, location.Row), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(leftKey, grid, GridCoordinate.Create(location.Column - 1, location.Row), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(downKey, grid, GridCoordinate.Create(location.Column, location.Row + 1), clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(upKey, grid, GridCoordinate.Create(location.Column, location.Row- 1), clickedValue);

    return countOfMarkedCells;
}

Now that we’ve done that, we can really do some cool things. Since we have a reference to a distinct location as an object, that object knows where it is. You can treat it like a plotter. Given a location, move up, down, left, right.(Note, we’ll probably want to rename this object to something like GridPlotter, so it makes more sense… this would have a GridCoordinate object, but for now, we’ll place the methods right in the GridCoordinate object:

public GridCoordinate MoveUp()
{
    return Create(Column, Row - 1);
}

public GridCoordinate MoveDown()
{
    return Create(Column, Row + 1);
}

public GridCoordinate MoveLeft()
{
    return Create(Column - 1, Row);
}

public GridCoordinate MoveRight()
{
    return Create(Column + 1, Row);
}

public string CreateCellKey()
{
    return CreateCellKey(this);
}

This lets you refactor code like follows. Instead of the +1 and –1 everywhere:

countOfMarkedCells += 
    IfContainsClickedValueThenMark(
    rightKey, grid, 
    GridCoordinate.Create(location.Column + 1, 
        location.Row), 
    clickedValue);

You can just put:

countOfMarkedCells += IfContainsClickedValueThenMark(
    rightKey, grid, 
    location.MoveRight(), 
    clickedValue);

At this point, unit tests all still pass, and app feels pretty good still, so this is a pretty successful refactoring process thus far. For the time until the next post, I’ll be making sure all areas of code referencing this class (the GridController class) no longer use pure “int column, int row” values, but rather use the more robust GridCoordinate class.

After this is complete, the GridCoordinate class will be renamed, and co-located near the Grid itself. Right now, this is only referenced by the GridController, but in reality, the GridCoordinate should serve as the base of all Grid cell lookups. (Rather than always using the Grid.Columns[column][row]:Cell).

Posted on 5/5/2009 6:42:40 AM by Jason Nadal

Permalink | Comments |

Categories: development | patterns | refactoring | codeQuality

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

An Exercise in Refactoring: Part 4

This post is a continuation of our previous article, a response to this smelly code.

When we last left off, the method was much smaller; we had extracted methods for the entire first half of the method. This article focuses on the bottom half. Note that the if blocks in the bottom half check to see that:

  1. The key is not empty.
  2. The cell corresponding to the key has not already been checked
  3. If 1&2 are fine, then check around THAT cell for matching cells (from the originally clicked cell)
  4. If there are any new connecting cells, add them to the scoring tally.

So, we can pull out a method and pass in the next one to check. Here’s our new method:

private int IfContainsClickedValueThenMark(string key, IGrid grid, int column, int row, CellValues clickedValue)
{
    int countOfMarkedCells = 0;
    if (key != String.Empty && !_searchedCells.Contains(key))
    {
        _searchedCells.Add(key, String.Empty);
        countOfMarkedCells += MarkConnectingCellsRecursive(grid, column, row, clickedValue);
    }
    return countOfMarkedCells;
}

Note that this is the first time in this series that we’ve actually reduced the number of lines of code. Most of the time, this will wind up happening, however in some cases (like this method), it takes some work to get the code to where you can actually see the similarities in logic. Duplicate code does not always look like duplicate code at first glance! Here’s the resulting code in the method we’re focusing on for the refactoring. We’ve also made the following changes:

  1. Changed “count” to something more meaningful: it’s actually the count of connected (marked) cells.
  2. We’ve added some descriptive comments. Note that this can mean that our code has room for further optimization. If our code needs the comments, how well is it written?
private int MarkConnectingCellsRecursive(IGrid grid, int column, int row, CellValues clickedValue)
{
    //set the current location to be clicked.
    grid.Columns[column][row].CellState = CellStates.Down;

    //we seed the count at one here, because at least the current cell is to be
    //marked.
    int countOfMarkedCells = 1; 

    //look all around our current cell for other cells of the same value.
    string rightKey = GetRightKey(grid, column, row, clickedValue);
    string leftKey = GetLeftKey(grid, column, row, clickedValue);
    string downKey = GetDownKey(grid, column, row, clickedValue);
    string upKey = GetUpKey(grid, column, row, clickedValue);

    //if the keys ARE the same value, look around them for the same value, mark them, and add them
    //to the count
    countOfMarkedCells += IfContainsClickedValueThenMark(rightKey, grid, column + 1, row, clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(leftKey, grid, column - 1, row, clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(downKey, grid, column, row + 1, clickedValue);
    countOfMarkedCells += IfContainsClickedValueThenMark(upKey, grid, column, row - 1, clickedValue);

    return countOfMarkedCells;
}

Posted on 5/4/2009 7:51:52 PM by Jason Nadal

Permalink | Comments |

Categories:

Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5