Download the code in C#
Download the code in Java
Check out my interview on .NET Rocks! – TDD on .NET and Java with Paul Mooney
For a brief overview, please refer to this post.
That’s the hard work done! Our WorkerDrones
can now deliver RobotParts
to their respective FactoryRoom
implementations. Now, we can start building Robots
! But wait…
It looks like our code is broken. Remember our WorkerDrone logic?
C#
public void IdentifyRobotPart(RobotPart robotPart) { switch (robotPart.RobotPartCategory) { case RobotPartCategory.Assembly: _transportMechanism = new AssemblyRoomTransportMechanism(); break; case RobotPartCategory.Weapon: _transportMechanism = new ArmouryTransportMechanism(); break; } }
Java
public void identifyRobotPart(robotPart robotPart) { switch (robotPart.getRobotPartCategory()) { case assembly: _transportMechanism = new assemblyRoomTransportMechanism(); break; case weapon: _transportMechanism = new armouryTransportMechanism(); break; } }
Our Unit Tests only ever considered WorkerDrones
that transport a single RobotPart
at a time. We haven’t considered the consequences of transporting multiple RobotParts
.
As it turns out, our WorkerDrone's
TransportMechanism
will be overwritten every time a RobotPart
is picked up. This will produce incorrect behaviour; essentially, all RobotParts
held by a WorkerDrone
will be delivered to the same FactoryRoom
implementation – the FactoryRoom
implementation associated with the last RobotPart
to be picked up, which will overwrite the previous RobotPart
that was picked up.
“How do we fix this?”
Well, currently our WorkerDrones
can only successfully transport a single RobotPart
at a time. Our Factory
would operate a lot more efficiently if our WorkerDrones
could carry multiple RobotParts
and successfully deliver them without having to return to the DeliveryBay
after each run.
First, let’s change the IdentifyRobotPart
method so that it simply returns a TransportMechanism
implementation based on the RobotPart
parameter:
C#
public TransportMechanism IdentifyRobotPart(RobotPart robotPart) { switch (robotPart.RobotPartCategory) { case RobotPartCategory.Assembly: return new AssemblyRoomTransportMechanism((Assembly) robotPart); case RobotPartCategory.Weapon: return new ArmouryTransportMechanism((Weapon) robotPart); } throw new NotImplementedException("I can't identify this component!"); }
Java
public transportMechanism identifyRobotPart(robotPart robotPart) throws UnsupportedOperationException { switch (robotPart.getRobotPartCategory()) { case assembly: return new assemblyRoomTransportMechanism((assembly) robotPart); case weapon: return new armouryTransportMechanism((weapon) robotPart); } throw new UnsupportedOperationException("I can't identify this component!"); }
We’ll call this method every time our WorkerDrone
picks up a RobotPart
. Next, let’s replace our TransportMechanism
member-variable with a collection:
C#
private readonly List<TransportMechanism> _transportMechanisms;
Java
private List<transportMechanism> _transportMechanisms;
Now we need to modify the OffLoadRobotPart
to simply transfer the RobotPart
that we just picked up to its associated FactoryRoom
instance.
C#
public FactoryRoom OffLoadRobotPart() { if (_factoryRoom == null) { EnterRoom(); } _factoryRoom.AddRobotPart(_robotPart); return _factoryRoom; }
Java
public E offLoadRobotPart() { if (_factoryRoom == null) { enterRoom(); } _factoryRoom.addRobotPart(_robotPart); return _factoryRoom; }
Note that we’ve also changed our TransportMechanism
abstraction to accept a RobotPart
by default. This is where we’ll load the RobotPart
that is picked up by the WorkerDrone
.
C#
protected TransportMechanism(RobotPart robotPart) { _robotPart = robotPart; }
Java
protected transportMechanism(U robotPart) { _robotPart = robotPart; }
Now, the most important part. We need to change the TransportRobotParts
method to loop through each TransportMechanism
(we’ll have 1 for each RobotPart that we picked up) and execute the OffLoadRobotPart
method:
C#
public void TransportRobotParts() { foreach (var transportMechanism in _transportMechanisms) { transportMechanism.OffLoadRobotPart(); } _transportMechanisms.Clear(); }
Java
public void transportRobotParts() { for (Iterator<transportMechanism> i = _transportMechanisms.iterator(); i.hasNext(); ) { transportMechanism transportMechanism = i.next(); transportMechanism.offLoadRobotPart(); } _transportMechanisms.clear(); }
OK. Now our WorkerDrone
can successfully deliver multiple RobotParts
to multiple FactoryRooms
. Now we need to ensure that it will return to the DeliveryBay
once it has delivered its payload.
AS usual with TDD, let’s start with the tests, and introduce a new test to assert that multiple RobotParts
can be delivered successfully. Our test will cover a scenario where a WorkerDrone
delivers multiple RobotParts
and then returns to the DeliveryBay
.
C#
public void WorkerDroneReturnsToDeliveryBayAfterDeliveringRobotParts() { WorkerDrone workerDrone = new MockedWorkerDrone(); var randomAssembly = new MockedAssembly(); var randomWeapon = new MockedWeapon(); workerDrone.PickUpRobotPart(randomAssembly); workerDrone.PickUpRobotPart(randomWeapon); workerDrone.TransportRobotParts(); Assert.True(workerDrone.GetTransportationMechanisms().Any()); var transportMechanism = workerDrone.GetTransportationMechanisms().First(); Assert.IsInstanceOf<DeliveryBayTransportMechanism>(transportMechanism); }
Java
public void workerDroneReturnsToDeliveryBayAfterDeliveringRobotParts() { workerDrone workerDrone = new mockedWorkerDrone(); robotPart randomAssembly = new mockedAssembly(); robotPart randomWeapon = new mockedWeapon(); workerDrone.pickUpRobotPart(randomAssembly); workerDrone.pickUpRobotPart(randomWeapon); workerDrone.transportRobotParts(); Iterator<transportMechanism> iterator = workerDrone.getTransportMechanisms().iterator(); assertTrue(iterator.hasNext()); transportMechanism transportMechanism = iterator.next(); assertThat(transportMechanism, instanceOf(deliveryBayTransportMechanism.class)); }
Notice here that we assert that our WorkerDrone
contains a single DeliveryBayTransportMechanism
. This implementation, when executed, simply returns the WorkerDrone
to the DeliveryBay
. Let’s look at how this works:
C#
public void TransportRobotParts() { foreach (var transportMechanism in _transportMechanisms) { transportMechanism.OffLoadRobotPart(); } _transportMechanisms.Clear(); var deliveryBayTransportMechanism = new DeliveryBayTransportMechanism(); _transportMechanisms.Add(deliveryBayTransportMechanism); deliveryBayTransportMechanism.EnterRoom(); }
Java
public void transportRobotParts() { for (Iterator<transportMechanism> i = _transportMechanisms.iterator(); i.hasNext(); ) { transportMechanism transportMechanism = i.next(); transportMechanism.offLoadRobotPart(); } _transportMechanisms.clear(); deliveryBayTransportMechanism deliveryBayTransportMechanism = new deliveryBayTransportMechanism(); _transportMechanisms.add(deliveryBayTransportMechanism); deliveryBayTransportMechanism.enterRoom(); }
Note that we explicitly create a DeliveryBayTransportMechanism
instance and execute the enterRoom
method. Now our WorkerDrone
has returned to DeliveryBay
after successful delivery of its payload. We’ve demonstrated how TDD and OOD can facilitate change during the software development life-cycle.
The next tutorial in the series will focus on building Robots
.
Connect with me: