Thursday, December 30, 2010

When thinking Design, think Jobs vs. Skills

I've been trying to get people to see that code should be built as layers of building blocks for some time. It's only just occurred to me how to explain another dimension of design that I see lacking most of the time in code I have to deal with.

It's what people refer to when they talk about building your language towards you domain. The idea is that you should be able to clearly concisely describe your problem domain in your language. If you can't they you end up with domain objects getting their hands dirty with implementation details.. like "how lists work in this language" or "managing back references" etc...

Here's my thought... Your building blocks should be separable into Skills and Jobs. Jobs are specific roles played by objects in your code. Skills are transferable, then include the details of how you manage to achieve things. You could reuse the skills in many different jobs.

When your domain objects are not <5 methods each with <5 lines... then see what skills your object has and factor them out.

Sometimes this will be extension methods on basic types (or lists, arrays, etc.), other times it's make a specific collection type that doesn't reference your domain, but identifies the relationship items of the collection have.

Does this make sense?
Do I need an example?
Is this just obvious?

Ok... needs an example.


I found this code on github... it's part of the Player class for a Battleships game.
   public void TakeTurnAutomated(Player otherPlayer)
        {
            bool takenShot = false;
            while (!takenShot)
            {
                int row = rnd.Next(GRID_SIZE);
                int col = rnd.Next(GRID_SIZE);

                if (EnemyGrid[row][col].Type == SquareType.Unknown)
                {
                    Fire(row, col, otherPlayer);
                    takenShot = true;
                }
            }
        }
The point I was trying to make is .. the intent of this code is to fire on the first random location that is 'unknown'... so it should say that. One way would be something like this...
public void TakeTurnAutomated(Player otherPlayer)
        {   
   Fire(RandomLocations.First(IsUnknown), otherPlayer);
        }
Or this...
public void TakeTurnAutomated(Player otherPlayer)
        {
   Fire(RandomUnknownLocation, otherPlayer);
        }

private Location RandomUnknownLocation
{ 
  get{
    return EnemyGrid.UnknownLocations.SelectOneAtRandom();
  }
}

Here.. SelectOneAtRandom becomes an extension method to IEnumerable. We don't care how it selects one at random.. as long as it doesn't take too long about it. Picking an item at random is a Skill we have... we use it in the Job of selecting a random unknown location. Any clearer?

Thursday, October 28, 2010

Why SVN Scared Me

I've worked with SVN very successfully with a bunch of companies... In short: We moved to Bzr. We had some issues. We moved back to SVN.. we cry every day.

Due to a drive from the developers in my team we moved to a distributed version control system. When I say 'moved' there was a stipulation from above that we keep using SVN as the repository of record. The perceived benefits were being able to make branches easily and work in sub-features in a more controlled manner. Being an 'XP' Nutter I didn't buy this.. "Integrate as often as possible".. you hear me cry. Still.. the developers wanted it.. so I was happy to give it a go.

We looked at a bunch of distributed version control solutions (mainly looking for something fast that will still work with SVN). We picked Bazaar.

We got to use it for a few months. I never really shared a branch with anyone.. but I did like working on a branch per feature.. being able to switch branches to do the refactoring I just found I need to do before my feature can continue.. etc.

Then we hit a snag... something in the SVN/Bazaar integration would sometimes cause SVN to store the wrong meta-data and all bazaar users would stop being able to commit changes. Management didn't really like the effort it took to get BZR working in the first place, so that was it's death toll.

We moved back to SVN and working off trunk. Everything goes ok. It's not very speedy, but hay... we can deal with that.

What get's me is this... BZR makes sure you commit before you merge from trunk. In SVN, when you get latest (and haven't committed yet).. SVN will merge what it can into your unbacked up files. More than anything, that's starting to really bother me.

Monday, March 15, 2010

Ruby BlankSlate Issue.. or The Problem Of Accidental Monkey Patching

I was updating a Rails app recently and found the tests weren't running.
The error was reported in HoboField (an ace gem that lets define your model fields within your model and auto-generate your migrations by 'diff'ing between schema and models).

The error was .../hobo_fields/field_declaration_dsl.rb:22 'field': wrong number of arguments (1 for 2)

This was being thrown when ruby was getting to a model class like this...


class SomeModel < ActiveRecord::Base
fields do
timestamps
end
...
end



The error turns out to be because the DSL class in Hobofields uses BlankSlate as it's base class. ActiveSupport also has a definition for BlankSlate, so Hobofields does an 'unless defined? BlankSlate' check before it defines one.

However... I was also using 'Riot' for my testing. Riot also defines BlankSlate.
The ActiveSupport version leaves the instance_eval method on the BlankSlate class. The Riot version removes it and doesn't check to see that BlankSlate is not already defined. Riot was accidentally Monkey Patching ActiveSupport.

As both libraries were being loaded, so the Riot definition was extending the ActiveSupport version and it was loosing it's instance_eval method. This leads to missing method picking up calls it shouldn't and eventually ends in the error I was getting.

I patched Riot and the problem went away. I expect to run into this sort of problem again in the future.

Do me a favor :) If you are developing a gem, please be really careful what you define in global space. It is best to namespace any classes you define so you don't risk cross over.

Thanks

Friday, March 12, 2010

Visual Studio 2010 Beta2 says "The application cannot start."

We have been having this issue with Visual Studio 2010 Beta2. It just stops loading. For some it stops loading unless you load it via loading a solution, others it's the opposite.

We can't move to the RC2 until we it supports Silverlight 4 properly..

The solution?

We have had some success by just loading up a visual studio command prompt and running:
devenv /ResetSettings

UPDATE:
Also try:
devenv /reset
devenv /resetuserdata


Give it a go. Let me know how you get on :)
(It might also be worth backing up your currentsettings.xml file first)

Monday, January 11, 2010

accepts_nested_attributes_for and sti

I have a site that lets you create child entities on the create form for the aggregate root. Thanks to accepts_nested_attributes_for this isn't too hard.

accepts_nested_attributes_for :kids, :allow_destroy => true

The problem I hit was mixing this with single table inheritance. When the child node is for a derived class, it saves without the 'type' information being set.

It seems the accepts_nested_attributes only creates new instances of the generic base class entity, which then ignores the value passed to the 'type' field.

I ended up doing this work around to get it to replace #new on the base class with an implementation that returns the correctly typed derived instance when the hash passed includes a 'type' field.

Thanks to Coderrr for posting this work around.

Note: I've backed up the code to http://gist.github.com/273858

GitHub Projects