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.

Following on from the previous tutorial, where we looked at Supplier and DeliveryBay objects, let’s move on to WorkerDrones .

Once again, here is our narrative:

“Mechs with Big Guns” is a factory that produces large, robotic vehicles designed to shoot other large, robotic vehicles. Robots are composed of several robotic parts, delivered by suppliers. Parts are loaded into a delivery bay, and are transported by worker drones to various rooms; functional parts such as arms, legs, etc., are dispatched to an assembly room. Guns and explosives are dispatched to an armoury.

The factory hosts many worker drones to assemble the robots. These drones will specialise in the construction of 1 specific robot, and will require all parts that make up that robot in order to build it. Once the drone has acquired all parts, it will enter the assembly room and build the robot. Newly built robots are transported to the armoury where waiting drones outfit them with guns. From time to time, two robots will randomly be selected from the armoury, and will engage one another in the arena, an advanced testing-ground in the factory. The winning robot will be repaired in the arena by repair drones. Its design will be promoted on a leader board, tracking each design and their associated victories.

Worker Drones

Worker Drones have several functions, as per the narrative, so we’re going to tackle the implementation one step at a time. It seems that at a fundamental level, the drones need to be able to differentiate between different RobotParts. For example, consider a situation in which the following set of robotic legs arrive in the DeliveryBay:

In that case, our WorkerDrone needs to transport these to the AssemblyRoom . However, check out the guns on this guy!!!

Those weapons, should they arrive in the DeliveryBay , need to be transported to the Armoury .

Essentially, WorkerDrone behaviour depends on the category that each RobotPart is associated with, and can change at runtime. Consider the following scenario:

WorkerDrone enters DeliveryBay and collects a RobotPart The RobotPart is identified as an item that belongs in the AssemblyRoom WorkerDrone transports the RobotPart to the AssemblyRoom WorkerDrone picks up the next RobotPart , which happens to be a Weapon WorkerDrone delivers this to the Armoury

WorkerDrones have 2 separate types of behaviour that govern RobotPart transportation. This is commonly known as the Strategy Design Pattern.

“Wait, that actually sounds like a Bridge pattern to me!”

Structurally, they are both the same. The difference is in their implementation. A Bridge is a behaviour that is set at design-time, and remains constant. A Strategy on the other hand, can change at runtime.

What we’re essentially doing is abstracting WorkerDrone transportation behaviour to separate classes that share the same abstraction. Our WorkerDrone simply chooses the correct implementation to suit a given scenario, and the implementation does the work.

Let’s keep thiings simple for the moment; we won’t actually implement the act of transporting RobotParts, we’ll simply implement the fundamental components involved.

To start, we need to identify RobotParts . So let’s tag them with a specific category – either Assembly , or Weapon . First, we need to define these categories:

C#



public enum RobotPartCategory { Assembly, Weapon }

Java

public enum robotPartCategory { assembly, weapon }

Now, we modify our RobotPart astraction to accept either of these categories:

C#



public abstract class RobotPart { private readonly RobotPartCategory _robotPartCategory; public RobotPartCategory RobotPartCategory { get { return _robotPartCategory; } } protected RobotPart(RobotPartCategory robotPartCategory) { _robotPartCategory = robotPartCategory; } }

Java

public abstract class robotPart { protected robotPartCategory robotPartCategory; public robotPartCategory getRobotPartCategory() { return robotPartCategory; } protected robotPart(robotPartCategory robotPartCategory) { this.robotPartCategory = robotPartCategory; } }

So far, so good. We spoke about different transportation behaviours. So let’s first create the abstraction:

C#



public abstract class TransportMechanism {}

Java

public abstract class transportMechanism {}

And both implementations; Assembly and Weapon. Remember: we’re not implementing any logic in these classes yet – we first need to ensure that our WorkerDrone can apply the correct behaviour based on each RobotPart :

C#



public class AssemblyRoomTransportMechanism : TransportMechanism {} public class ArmouryTransportMechanism : TransportMechanism {}

Java

public class assemblyRoomTransportMechanism extends transportMechanism {} public class armouryTransportMechanism extends transportMechanism {}

Now let’s implement a simple method that allows our WorkerDrone to choose the manner in which it will transport a given RobotPart , by first examining that RobotPart

C#

public abstract class WorkerDrone { public TransportMechanism TransportMechanism { get; private set; } public void IdentifyRobotPart(RobotPart robotPart) { switch (robotPart.RobotPartCategory) { case RobotPartCategory.Assembly: TransportMechanism = new AssemblyRoomTransportMechanism(); break; case RobotPartCategory.Weapon: TransportMechanism = new ArmouryTransportMechanism(); break; } } }

Java

public class workerDrone { private transportMechanism _transportMechanism; public transportMechanism getTransportMechanism() { return _transportMechanism; } public void identifyRobotPart(robotPart robotPart) { switch (robotPart.getRobotPartCategory()) { case assembly: _transportMechanism = new assemblyRoomTransportMechanism(); break; case weapon: _transportMechanism = new armouryTransportMechanism(); break; } } }

As usual, our WorkerDrone is an abstraction and we apply a mocked implementation in our associated unit test:

C#



public void WorkerDroneIdentifiesRobotPart() { var robotPart = new MockedRobotPart(RobotPartCategory.Assembly); var workerDrone = new MockedWorkerDrone(); workerDrone.IdentifyRobotPart(robotPart); Assert.IsInstanceOf<AssemblyRoomTransportMechanism>(workerDrone.TransportMechanism); robotPart = new MockedRobotPart(RobotPartCategory.Weapon); workerDrone.IdentifyRobotPart(robotPart); Assert.IsInstanceOf<ArmouryTransportMechanism>(workerDrone.TransportMechanism); }

Java

public void workerDroneIdentifiesRobotPart() { robotPart robotPart = new mockedRobotPart(robotPartCategory.assembly); workerDrone workerDrone = new mockedWorkerDrone(); workerDrone.identifyRobotPart(robotPart); assertThat(workerDrone.getTransportMechanism(), instanceOf(assemblyRoomTransportMechanism.class)); robotPart = new mockedRobotPart(robotPartCategory.weapon); workerDrone.identifyRobotPart(robotPart); assertThat(workerDrone.getTransportMechanism(), instanceOf(armouryTransportMechanism.class)); }

Let’s walk through this test:

We instnantiate a new RobotPart as an Assembly We instatiate our WorkerDrone The WorkerDrone examines the RobotPart and instantiates its TransportationMechanism appropriately We assert that the correct TransportationMechanism behavioural implementation has been applied for the Assembly RobotPart We repeat the process for a Weapon RobotPart

In the next tutorial, we’ll implement the logic involved in transporting RobotParts from the DeliveryRoom to both AssemblyRoom and Armoury .

Connect with me: