In my latest robocode series I've been exploring a decision making bounded context and have fused together many elements to make this happen. Here's what I've done so far:
- written acceptance testing and statistics writing for assertions
- introduced IObservable<IEvent<T>> into my robocode design
- recognised that I have multiple contexts that I hope can have their own dialect
- introduced a world state
- created a decision making bounded context with a state keeper and updater
- created observer(s) to watch this decision state
- created a context translator to push world state updates into the decision dialect
It has been quite a lot of code in a few posts (but that's what my blog is mostly about - the finer details of development and shaping code).
I'm now ready to hook the whole thing up and I need my robot engine to activate the context translators when a world state update occurs.
As usual I start with specifications for this class and I'm going to add two new ones into an existing specification:
public class when_told_to_ensure_the_world_state_is_up_to_date : with_a_robot_engine { Establish context = () => { robotEngine.Initialise(robot); expectedGunTemperature = 222; worldStateUpdate = w => w.GunTemperature = expectedGunTemperature; robotEngine.PublishToTheWorldState(worldStateUpdate); }; Because of = () => robotEngine.EnsureTheWorldStateIsUpToDate(); It should_update_the_world_state_accordingly = () => worldState.GunTemperature.ShouldEqual(expectedGunTemperature); It should_have_no_pending_world_state_updates_left = () => robotEngine.PendingWorldStateUpdates.Count.ShouldEqual(0); --> It should_inform_the_first_translator_of_the_world_state_change = () => firstContextTranslator.AssertWasCalled(x => x.PublishAStateChange(worldState)); --> It should_inform_the_second_translator_of_the_world_state_change = () => secondContextTranslator.AssertWasCalled(x => x.PublishAStateChange(worldState)); static Action<IWorldState> worldStateUpdate; static int expectedGunTemperature; }
I've now introduced two new context translators into the specification and when the world state is to be updated I want them informed of this world state change.
The change in the constructor of the robocode engine in my specification is as follows:
firstContextTranslator = MockRepository.GenerateStub<IContextTranslator>(); secondContextTranslator = MockRepository.GenerateStub<IContextTranslator>(); var contextTranslators = new List<IContextTranslator> {firstContextTranslator, secondContextTranslator}; robotEngine = new RobotEngine(worldState, contextTranslators, statisticsManager);
To implement this there is a new loop in this method in the robot engine:
public void EnsureTheWorldStateIsUpToDate() { foreach(var makeAnUpdateTo in pendingWorldStateUpdates) { makeAnUpdateTo(worldState); } pendingWorldStateUpdates.RemoveAll(a => true); foreach (var contextTranslator in contextTranslators) { contextTranslator.PublishAStateChange(worldState); } }
I'm happy with the code so far, but this method above doesn't necessarily scan right and I think this needs addressing.
I'm a huge fan of the Clean Code book by Bob Martin and I'm a proud wearer of the green wristband, (seeking "craftsmanship over crap"!) and at every opportunity I look to make my code intuitive and readable. So with a few simple method extractions I think the above can be made simpler so that when I read it back at a later date, the code is intention revealing.
As you can see I believe this applies as much to experimentation and personal projects as it does to professional code bases in every day work
Here's the end result:
public void EnsureTheWorldStateIsUpToDate() { ConsumePendingUpdatesByUpdatingTheWorldState(); PublishTheWorldStateToTheContextTranslators(); } void ConsumePendingUpdatesByUpdatingTheWorldState() { foreach(var makeAnUpdateTo in pendingWorldStateUpdates) { makeAnUpdateTo(worldState); } pendingWorldStateUpdates.RemoveAll(a => true); } void PublishTheWorldStateToTheContextTranslators() { foreach (var contextTranslator in contextTranslators) { contextTranslator.PublishAStateChange(worldState); } }
So I'm now ready to run my acceptance tests which will run some battles and ensure that my implementation does what I think it should. The next post will cover this acceptance and debugging exercise.
0 comments: