Tuesday, March 22, 2011

PS3 Story on Target

I got interviewed by Target the other day, so I might be on TV3 some Tue soon. They have a few other customer stories and they're going to ring Sony directly, so it should be interesting to watch.

I took down the PDF of the court ruling because it had my name, address and phone number. TV3 suggested it might be a good idea before the programme's screened. If you need it, let me know and I'll black out the personal bits and republish.

Otherwise, the main posts and comments for the PS3 hearing are here and here.

Sunday, January 31, 2010

The Repository Pattern and Linq

I've just been around the block with the Repository pattern and Linq, and here are my thoughts.

The Repository pattern is designed to hide all data access concerns behind a facade, so for clients of the repository, quite simply, there is no Linq. I've seen a lot of people try to have their cake and eat it too, where they put the repository pattern in place then try all manner of witchcraft to let the client use Linq through it. This is bad. This is ice-skating uphill. Don't do it.

If you have a small project and want to use Linq for business logic, don't use the Repository pattern. If you have a large project and want a testable facade, or a storage agnostic repository, use the pattern, but make sure you pass domain objects to it, not data access objects.

Wednesday, November 11, 2009

Blogging velocity has slowed somewhat. Probably because there was so much code to post on my Rx Framework attempt I ran out of gas, then we had a baby. So bugger it, I wont post nothing on it and continue as if I wasn't going to.

So, I'm on the deck bbqing some chicken with a beer and laptop.  What to post?

I'll reflect on work. Fun times.

Over the last several years I've been building an Ajax UI framework for porting Windows applications to the web.  The first version only worked in IE and I've been working on the next version which will, theoretically, work in all browsers. But now a client is paying to have the first version work in FireFox and Safari as well.

I can't say I mind this too much and it (touch-wood) is turning out to be easier than I though.  Although I realise the devil is in IE6.

First step: Consistent look in IE7+, FF and Safari. Get everything looking the same- it doesn't have to be pretty but if all browsers start with the same foundation that isn't too bad, it can be improved from there.

This turned out easier than I thought by using the strict doctype and a good CSS reset. Removing any -moz- styles and the odd JavaScript fix for FF.

Second step: Consistent behaviour.

Third step: Carefully get it working in IE6.

Fourth step: Make it pretty.

My chicken is burning.

Sunday, July 19, 2009

.NET Reactive Framework

In our game, stories are first-class beings -  invisible meddlers. They move around, observe and manipulate the world according to their plot. They wait for triggering conditions or patterns of events to occur, and then spring to life to advance the story.

In our case, we have an evil story waiting for the betrayal of a paladin. When this happens, the story will unravel a series of events, involving a horror, a child's toy and a man with a poisoned name.

How does the story listen out for the betrayal? Our game follows an event-driven design so we'll need to do some event-processing.

I recently saw Erik Meijer's presentation on .NET's up and coming Reactive Framework (Rx), which looks pretty cool and the right tool for the job. There are already a couple of other reactive programming frameworks out there, like Continous LINQ and Reactive LINQ, but I thought it would be interesting to see what's involved in writing one, so here we go.

Rx is the mathematical inverse of IEnumerable, dubbed IObservable. So instead of being able to iterate over a stream of objects you get to listen to a stream of events.

No big deal really, but the cool thing is that it supports Linq, so you can query over event streams.

IObservable<ICombatCommand> paladinFleeCmds =
  from cmd in CombatMediator.CombatCommands
  where cmd.CommandType == CommandType.Flee &&
        cmd.Subject.CharacterType == CharacterType.Paladin
  select cmd;

paladinFleeCmds.Subscribe(cmd =>
  {
    IStoryPoint betrayal = new Betrayal(cmd.Subject);
    story.RegisterStoryPoint(betrayal);
  });

I'll post how to implement this kind of Linq-to-Events shortly.

Sunday, June 28, 2009

db4o - an Object-Database


Versant have come up with an object-database that is very easy to use.


In some cases, performance might not be there when you need it, but when this isn't a problem the benefits easily stack up.


const string dbFile = @"C:\FearlessOnes\db\Monsters.db";

IMonster monster = new Horror("Atlach-Nacha",
"Spinner in Darkness");

// If the database doesn't exist, it will be created
using (IObjectContainer db = Db4oFactory.OpenFile(dbFile))
{
db.Store(monster);
}

Nice and easy. Linq is also supported,


using (IObjectContainer db = Db4oFactory.OpenFile(dbFile))
{
monster = (from IMonster m in db.Query(typeof(IMonster))
where m.Name == "Atlach-Nacha"
select m).Single();

monster.Mode = MonsterMode.Hungry;
db.Store(monster);
}

Just reference one DLL, or two if you want to use Linq, and you're smiling.


A great tool for developing the domain layer, even if you plan to use a relational database later. Download it here. The documentation is also very good.


Inversion of Control (IoC)

In our game, a monster wakes up because some players have entered its domain. It's in a dark corner, so it waits for the players to pass by and then lunches them.


Assuming the flow of control starts with the monster object, how does it communicate its intentions to the combat system?


1) The direct approach would be for the monster to create and reference the combat system.



public class Monster : ICreature
{
private DefaultCombatSystem combatSystem =
new DefaultCombatSystem(); // Bad

protected void GetUpToNoGood()
{
//...
ICommand command = new LunchPlayers(players);
IResponse response =
combatSystem.HandleCommand(this, command);
//...
}
//...
}

But this comes with a raft of problems. The least of which is when the combat system changes, the monster will need updating.


2) A better approach is to refer to an interface for combat systems rather than a particular implementation. This will save on the refactoring. The monster can also ask a factory for the combat system that implements this interface. This adds a dependency on a factory however, which is not ideal.


So instead we inject the combat system into the monster. This is known as dependency injection and removes the monster's dependency on a concrete combat system or factory.




public class Monster : ICreature
{
private ICombatSystem combatSystem; // Better

// Constructor injection
public Monster(ICombatSystem combatSystem)
{
this.combatSystem = combatSystem;
}
//...
}

This would be perfect for things a monster does depend on, like its stomach, but the combat system doesn't belong here.


3) An even better approach is to expose an event that the monster fires. This frees the monster from combat system concerns altogether, so it can concentrate on what monsters do best.




public delegate void CommandHandler(ICommand command);

public class Monster : ICreature
{
public event CommandHandler Command; // Best

protected void GetUpToNoGood()
{
//...
ICommand command = new LunchPlayers(players);
Command(this, command);
//...
}
//...
}

Wiring up the event,




public class DefaultCombatSystem : ICombatSystem
{
private IList<ICreature> creatures;

public void AddCreature(ICreature creature)
{
creatures.Add(creature);
creature.Command += OnCommand;
}

public void OnCommand(ICommand command)
{
// Handle the command
}
//...
}

This is the observer design pattern using events.


Control has shifted to the combat system now. It references the monster and will make the required calls to resolve the combat. This is known as inversion of control (IoC) or the Hollywood principle - "Don't call us, we'll call you".


In hindsight it's easy to see we had things backwards from the start, but the direct approach is all too common. It's a hangover from procedural programming where the programme's flow of control acts directly rather than setting up an object model and then handing over control to one of these objects.


In this way, the combat system and the monster are much easier to maintain, test and debug.

Friday, June 26, 2009

Disputes Tribunal Ruling

I found the ruling from the tribunal. Here's the guts of it (I don't have a scanner):


The Tribunal hereby orders

Sony Computer Entertainment New Zealand Ltd and Electronics Boutique Australia Pty Ltd are jointly and severally liable to pay $375 to Mr Stevens within ten days of the date of this order.


Reasons


[snip]


It was proved that the Mr Stevens' playstation was not of acceptable quality:

  1. An article from the Consumer magazine (a respected organisation) in July 2006 was reliable evidence (based on surveys, information from manufacturers and retailers and their own experience) that a reasonable life expectancy for appliances was five years for computers and 8+ years for DVD players (both playstation functions).
  2. No appliance had a life expectancy of less than two years and there was no reason to suppose that playstations were less durable than other appliances, in particular when playstations were used for games and received hard treatment from children.
  3. Mr Stevens' playstation, by failing within two years, was therefore not fit for all of its purposes, not free of minor defects and not durable.
  4. It was not an answer that (as stated in the article) the life expectancy of appliances was getting shorter... because this did not account for difference between the stated life expectancy (eg computers, five years) and the failure in Mr Stevens' case (under two years).
  5. It was not an answer that the technological complexity of playstations meant that failure of a small percentage of units was "inevitable" (letter to Mr Stevens) if there was nonetheless a failure of the guarantee of acceptable quality.
  6. It was also not an answer that Mr Stevens' warranty was limited to one year as it was not possible to contract out of the Consumer Guarantees Act in non-business transactions (section 43).

[snip]


There were five arguments I raised, but only two were accepted. I can post them if anyone's interested.