Category Archives: design patterns

Automatically Recover from Connectivity Failure in Go

Fork me on GitHubGo Fallback
Go provides a very effective means of executing HTTP requests, exposed through the net/http package. In certain scenarios, it may be favourable to provide recovery mechanisms that allow Go applications to function as normal in the event of HTTP connectivity failure.

Assuming the following process flow:

Application with dependency

Application with dependency

Our Go application has an explicit dependency on System 1. Let’s assume that communication between both systems occurs over HTTP, and that our Go application must be highly available; in that it must remain functional if downstream systems fail:

Application with fallback dependencies

Application with fallback dependencies

Now we must configure our Go application, in the event of System 1 failure; to automatically attempt to repeat the failing HTTP request to Backup 1.

Similarly, should the connection to Backup 1 become unavailable, our Go application should attempt to repeat the failing HTTP request to Backup 2.

Chain of Responsibility

Chain of Responsibility

Chain of Responsibility

The Chain of Responsibility design pattern provides a suitable solution. The pattern defines a series of handlers, each one designed to execute a specific task. The task is passed along the chain until a handler successfully completes the task, or all handlers fail to complete the task.

Package fallback is designed to achieve this pattern, and to provide redundancy in the event of connectivity failure. The process flow pertaining to the above example is as follows:

Fallback process flow

Fallback process flow

Builder

Builder Design Pattern

Builder Design Pattern

Package fallback handles the execution of HTTP requests by wrapping each request in a ConnectionBuilder, as per the Builder design pattern. This allows the developer control over the manner in which underlying HTTP requests are constructed. Each ConnectionBuilder controls an underlying Connection – a wrapper for the actual HTTP request.

Each HTTP request is wrapped in a ConnectionBuilder, and managed by a ConnectionManager –  the Director component. Once the Chain of Responsibility has been established, the ExecuteHTTPRequest method is executed on the first underlying Connection. This method will attempt to successfully execute the underlying HTTP request. Should the request fail, the method will invoke the ExecuteHTTPRequest on the next Connection in the chain, in a recursive manner, until any given request succeeds, or all requests fail.

Installation

go get github.com/daishisystems/fallback

Sample Code

First, we need to establish some data structures:

    // BasicResponse represents the response issued from the first successful
    // HTTP request, if applicable.
    type BasicResponse struct {
        Text   string
        Detail string
    }

    // BasicError represents the error issued from the last unsuccessful
    // HTTP request, if applicable.
    type BasicError struct {
        Code    int
        Message string
    }

    //
    type PostBody struct {
        Name   string
        Amount int
    }

    basicResponse := &BasicResponse{}
    basicError := &BasicError{}

    postBody := PostBody{
        "Random", 100,
    }

Next, we initialise a ConnectionManager (the Director in our Builder implementation) and pass a ConnectionBuilder (the Builder itself). This particular ConnectionBuilder, in this example, is designed to execute successfully:

    passPath := "http://demo7227109.mockable.io/get-basic"
    failPath2 := "http://demo7227109.mockable.io/fail-basic"
    failPath1 := "http://demo7227109.mockable.io/fail-basic-post"

    connectionManager := fallback.ConnectionManager{}

    // This Connection will execute last, and will succeed.
    passBuilder := fallback.NewConnectionBuilder("PASS", "GET", passPath, true,
        nil, nil, &basicResponse, &basicError, nil)
    connectionManager.CreateConnection(passBuilder)

Now add 2 more ConnectionBuilders to the chain, both of which, in this example, are designed to fail execution:

    // This Connection will be the 2nd Connection to execute, and will fail.
    failBuilder2 := fallback.NewConnectionBuilder("FAIL2", "POST", failPath2,
        true, nil, nil, &basicResponse, &basicError, passBuilder.Connection)
    connectionManager.CreateConnection(failBuilder2)

    //This Connection will be the 1st Connection to execute, and will fail.
    failBuilder1 := fallback.NewConnectionBuilder("FAIL1", "POST", failPath1,
        true, postBody, nil, &basicResponse, &basicError,
        failBuilder2.Connection)
    connectionManager.CreateConnection(failBuilder1)

Finally, invoke the first ConnectionBuilder in the chain. In this example, both first and second ConnectionBuilders are designed to fail. Execution recursively falls back to passBuilder, which executes successfully:

    // Each Connection will be invoked in a recursive manner until a
    // Connection succeeds, or all Connections fail. Please refer to the Chain
    // of Responsibility design for more information.
    statusCode, err := failBuilder1.Connection.ExecuteHTTPRequest()
    if err != nil {
        panic(err)
    }

    fmt.Printf("HTTP status code: %d\n", statusCode)
    fmt.Printf("Text: %s\n", basicResponse.Text)
    fmt.Printf("Detail: %s", basicResponse.Detail)

The fall-back process is encapsulated within package fallback; consuming clients are unaware of failures, and instead, are guaranteed success, long as at least one HTTP connection remains viable.

Summary

Package fallback enhances the durability of your API by automatically recovering from connectivity failure. It achieves this by providing an enhanced degree of redundancy to HTTP requests, introducing a Chain of Responsibility, consisting of a series of fallback HTTP requests designed to augment an initial HTTP request. Should the initial HTTP request fail, the next fallback HTTP request in the chain will execute.

Any number of fallback HTTP requests can be chained sequentially. Redundancy is achieved by executing each fallback HTTP request in a recursive manner until one of the requests succeeds, or all requests fail.

Connect with me:

RSSGitHubTwitter
LinkedInYouTubeGoogle+

Object Oriented, Test Driven Design in C# and Java: A Practical Example Part #5

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.

Overview

In the last tutorial we focused on correcting some logic in our classes and tests. It’s about time that we started building Robots. Let’s start with a simple example consisting of the following components:

  • Head
  • Torso
  • 2x Arms
  • 2x Legs

Not the most exciting contraption, but we can expand on this later. For now, let’s define these simple properties and combine them in a simple class called Robot:

C#

    public abstract class Robot {
        public Head Head { get; set; }
        public Torso Torso { get; set; }
        public Arm LeftArm { get; set; }
        public Arm RightArm { get; set; }
        public Leg LeftLeg { get; set; }
        public Leg RightLeg { get; set; }
    }

Java

public abstract class robot {
    private head _head;
    private torso _torso;
    private arm _leftArm;
    private arm _rightArm;
    private leg _leftLeg;
    private leg _rightLeg;

    public head getHead() {
        return _head;
    }

    public void setHead(head head) {
        _head = head;
    }

    public torso getTorso() {
        return _torso;
    }

    public void setTorso(torso torso) {
        _torso = torso;
    }

    public arm getLeftArm() {
        return _leftArm;
    }

    public void setLeftArm(arm leftArm) {
        _leftArm = leftArm;
    }

    public arm getRightArm() {
        return _rightArm;
    }

    public void setRightArm(arm rightArm) {
        _rightArm = rightArm;
    }

    public leg getleftLeg() {
        return _leftLeg;
    }

    public void setLeftLeg(leg leftLeg) {
        _leftLeg = leftLeg;
    }

    public leg getRightLeg() {
        return _rightLeg;
    }

    public void setRightLeg(leg rightLeg) {
        _rightLeg = rightLeg;
    }
}

“Wait! Why are you writing implementation-specific code? This is about TDD! Where are your unit tests?”

If I write a class as above, I can expect that it will work because it’s essentially a template, or placeholder for data. There is no logic, and very little, if any scope for error. I could write unit tests for this class, but what would they prove? There is nothing specific to my application, in terms of logic. Any associated unit tests would simply test the JVM (Java) or CLR (.NET), and would therefore be superfluous.

Disclaimer: A key factor in mastering either OOD or TDD is knowing when not to use them.

Let’s start building Robots. Robots are complicated structures composed of several key components. Our application might grow to support multiple variants of Robot. Imagine an application that featured thousands of Robots. Assembling each Robot to a unique specification would be a cumbersome task. Ultimately, the application would become bloated with Robot bootstrapper code, and would quickly become unmanageable.

“Sounds like a maintenance nightmare. What can we do about it?”

Ideally we would have a component that created each Robot for us, with minimal effort. Fortunately, from a design perspective, a suitable pattern exists.

Introducing the Builder Pattern

We're here to build your robots, sir!

We’re here to build your robots, sir!

The Builder pattern provides a means to encapsulate the means by which an object is constructed. It also allows us to modify the construction process to allow for multiple implementations; in our case, to create multiple variants of Robot. In plain English, this means that an application the leverages a builder component does not need to know anything about the object being constructed.

Builder Design Pattern

Builder Design Pattern

“That sounds great, but isn’t the Builder pattern really just about good house-keeping? All we really achieve here is separation-of-concerns, which is fine, but my application is simple. I just need a few robots; I can assemble these with a few lines of code.”

The Builder pattern is about providing an object-building schematic. Let’s go through the code:

C#

    public abstract class RobotBuilder {
        protected Robot robot;

        public Robot Robot { get { return robot; } }

        public abstract void BuildHead();
        public abstract void BuildTorso();
        public abstract void BuildArms();
        public abstract void BuildLegs();
    }

Java

public abstract class robotBuilder {
    protected robot robot;

    public robot getRobot() {
        return robot;
    }

    public abstract void buildHead();

    public abstract void buildTorso();

    public abstract void buildArms();

    public abstract void buildLegs();
}

This abstraction is the core of our Builder implementation. Notice that it provides a list of methods necessary to construct a Robot. Here is a simple implementation that builds a basic Robot:

C#

    public class BasicRobotBuilder : RobotBuilder {
        public BasicRobotBuilder() {
            robot = new BasicRobot();
        }

        public override void BuildHead() {
            robot.Head = new BasicHead();
        }

        public override void BuildTorso() {
            robot.Torso = new BasicTorso();
        }

        public override void BuildArms() {
            robot.LeftArm = new BasicLeftArm();
            robot.RightArm = new BasicRightArm();
        }

        public override void BuildLegs() {
            robot.LeftLeg = new BasicLeftLeg();
            robot.RightLeg = new BasicRightLeg();
        }
    }

Java

public class basicRobotBuilder extends robotBuilder {

    public basicRobotBuilder() {
        robot = new basicRobot();
    }

    @Override
    public void buildHead() {
        robot.setHead(new basicHead());
    }

    @Override
    public void buildTorso() {
        robot.setTorso(new basicTorso());
    }

    @Override
    public void buildArms() {
        robot.setLeftArm(new basicLeftArm());
        robot.setRightArm(new basicRightArm());
    }

    @Override
    public void buildLegs() {
        robot.setLeftLeg(new basicLeftLeg());
        robot.setRightLeg(new basicRightLeg());
    }
}

It’s not your application’s job to build robots. It’s your application’s job to manage those robots at runtime. The application should be agnostic in terms of how robots are provided. Let’s add another Robot to our application; this time, let’s design the Robot to run on caterpillars, rather than legs.

Caterpillar Robot

Caterpillar Robot

First, we introduce a new class called Caterpillar. Caterpillar must extend Leg, so that it’s compatible with our Robot and RobotBuilder abstractions.

C#

    public class Caterpillar : Leg {}

Java

public class caterpillar extends leg {

}

This class doesn’t do anything right now. We’ll implement behaviour in the next tutorial. For now, let’s provide a means to build our CaterpillarRobot.

C#

    public class CaterpillarRobotBuilder : RobotBuilder {
        public CaterpillarRobotBuilder() {
            robot = new CaterpillarRobot();
        }

        public override void BuildHead() {
            robot.Head = new BasicHead();
        }

        public override void BuildTorso() {
            robot.Torso = new BasicTorso();
        }

        public override void BuildArms() {
            robot.LeftArm = new BasicLeftArm();
            robot.RightArm = new BasicRightArm();
        }

        public override void BuildLegs() {
            robot.LeftLeg = new Caterpillar();
            robot.RightLeg = new Caterpillar();
        }
    }

Java

public class caterpillarRobotBuilder extends robotBuilder {
    public caterpillarRobotBuilder() {
        robot = new caterpillarRobot();
    }

    @Override
    public void buildHead() {
        robot.setHead(new basicHead());
    }

    @Override
    public void buildTorso() {
        robot.setTorso(new basicTorso());
    }

    @Override
    public void buildArms() {
        robot.setLeftArm(new basicLeftArm());
        robot.setRightArm(new basicRightArm());
    }

    @Override
    public void buildLegs() {
        robot.setLeftLeg(new caterpillar());
        robot.setRightLeg(new caterpillar());
    }
}

Notice that all methods remain the same, with the exception of BuildLegs, which now attaches Caterpillar objects to both left and right legs. We create an instance of our CaterpillarRobot as follows:

C#

    var caterpillarRobotBuilder = new CaterpillarRobotBuilder();

    caterpillarRobotBuilder.BuildHead();
    caterpillarRobotBuilder.BuildTorso();
    caterpillarRobotBuilder.BuildArms();
    caterpillarRobotBuilder.BuildLegs();

Java

        caterpillarRobotBuilder caterpillarRobotBuilder = new caterpillarRobotBuilder();
        caterpillarRobotBuilder.buildHead();
        caterpillarRobotBuilder.buildTorso();
        caterpillarRobotBuilder.buildArms();
        caterpillarRobotBuilder.buildLegs();

“That’s still a lot of repetitive code. Your CaterpillarRobot isn’t that much different from your BasicRobot. Why not just extend CaterpillarRobotBuilder from BasicRobotBuilder?”

Yes, both classes are similar. Here, you must use your best Object Oriented judgement. If your classes are unlikely to change, then yes, extending BasicRobotBuilder to CaterpillarRobotBuilder might be a worthwhile strategy. However, you must consider the cost of doing this, should future requirements change. Suppose that we introduce a fundamental change to our CaterpillarRobot class, such that it no longer resembles, nor behaves in the same manner as a BasicRobot. In that case, we would have to extract the CaterpillarRobotBuilder class from BasicRobotBuilder, and extend if from RobotBuilder, which may involve significant effort.
As regards repetitive code, let’s look at a means of encapsulating this further, in what’s called a Director. The Director’s purpose is to invoke the Builder’s methods to facilitate object construction, encapsulating construction logic, and removing the need to implement build methods explicitly:

C#

    public class RobotConstructor {
        public void Construct(RobotBuilder robotBuilder) {
            robotBuilder.BuildHead();
            robotBuilder.BuildTorso();
            robotBuilder.BuildArms();
            robotBuilder.BuildLegs();
        }
    }

Java

    public void Construct(robotBuilder robotBuilder) {
        robotBuilder.buildHead();
        robotBuilder.buildTorso();
        robotBuilder.buildArms();
        robotBuilder.buildLegs();
    }

Now our build logic is encapsulated within a controlling class, which is agnostic in terms of the actual implementation of robotbuilder – we can load any implementation we like, and our constructor will just build it.

C#

            var robotConstructor = new RobotConstructor();
            var basicRobotBuilder = new BasicRobotBuilder();

            robotConstructor.Construct(basicRobotBuilder);

Java

        robotConstructor robotConstructor = new robotConstructor();
        basicRobotBuilder basicRobotBuilder = new basicRobotBuilder();

        robotConstructor.Construct(basicRobotBuilder);

Summary

We’ve looked at the Builder pattern in this tutorial, and have found that it is an effective way of:

  • Providing an abstraction that allows multiple robot types to be assembled in multiple configurations
  • Encapsulates robot assembly logic
  • Facilitates the instantiation of complex, composite objects

In the next tutorial in this series we’ll focus on making robots fight.

Connect with me:

RSSGitHubTwitter
LinkedInYouTubeGoogle+

Object Oriented, Test Driven Design in C# and Java: A Practical Example Part #3

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.

We’ve provided our WorkerDrones with a means to determine an appropriate method of transportation by inspecting any given RobotPart implementation. Now WorkerDrones may select a TransportationMechanism implementation that suits each RobotPart. But we have yet to implement the actual logic involved. This is what we’ll cover in this tutorial. Look at how eager the little guy is! Let’s not delay; he’s got plenty of work to do.

WorkerDrone

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.

Let’s look at what exactly happens when we transport a RobotPart.
First, the WorkerDrone needs to identify the RobotPart that it just picked up, so that it can transport the part to the correct FactoryRoom. Let’s dive right in.

In the previous tutorial, we defined a means to do this by examining a RobotPart's RobotPartCategory and returning an appropriate TransportMechanism. Now, let’s add logic to our TransportMechanism.

First, we need to keep track of the FactoryRoom where we’ll offload the RobotParts:

C#

 private FactoryRoom _factoryRoom;

Java

    private E _factoryRoom;

First of all, what can we tell about the difference between both implementations? Both contain private properties, but our C# implementation is explicitly bound to a FactoryRoom object. Our Java implementation, on the other hand, seems to be bound to the letter “E”.

“What’s that all about?”

The difference in implementations can be explained by discussing Generics. Essentially, Generics allow us to define an action, like a method, without defining a concrete return-type or input parameter – instead, we define these in concrete implementations of our abstraction. At this point, rather than go off-topic, I’ll provide a link to a thorough tutorial on this subject in C#.

In a nutshell, the difference in implementations comes down to a personal preference – I prefer Java’s implementation of Generics over C#’s, specifically Java’s support for covariance and contravariance. Again, I’m happy to follow up with this offline, or to host a separate post on the subject, but for now, let’s keep going.

Let’s look at our Java implementation of transportMechanism:

public abstract class transportMechanism<E extends factoryRoom, U extends robotPart> {

Here, we’re telling the compiler that our transportMechanism class will require 2 concrete implementations, both of which should be derived from factoryRoom and robotPart respectively. To illustrate this, let’s look at armouryTransportMechanism, a class derived from transportMechanism in Java:

public class armouryTransportMechanism extends transportMechanism<armoury, weapon> {

    @Override
    public armoury getFactoryRoom() {
        return new armoury();
    }
}

Notice our Generic implementation of factoryRoom and robotPart map to armoury and weapon, respectfully.

I’ll cover more about Generics on request. For now, let’s co back to our design.

Our TransportMechanism needs to return an appropriate FactoryRoom:

C#

public abstract FactoryRoom GetFactoryRoom();

Java

public abstract E getFactoryRoom();

So, what actually happens when a WorkerDrone moves RobotParts to a FactoryRoom? The WorkerDrone needs to enter the FactoryRoom, and then offload its components into the FactoryRoom:

C#

        public void EnterRoom() {
            _factoryRoom = GetFactoryRoom();
            _factoryRoom.AddTransportationMechanism(this);
        }

        public FactoryRoom OffLoadRobotParts(List<RobotPart> robotParts) {
            if (_factoryRoom == null) {
                EnterRoom();
            }
            _factoryRoom.SetRobotParts(new List<RobotPart>(robotParts));
            robotParts.Clear();

            return _factoryRoom;
        }

Java

    public void enterRoom() {
        _factoryRoom = getFactoryRoom();
        _factoryRoom.addTransportationMechanism(this);
    }

    public E offLoadRobotParts(List<U>robotParts) {
        if (_factoryRoom == null) {
            enterRoom();
        }
        _factoryRoom.setRobotParts(new ArrayList<U>(robotParts));
        robotParts.clear();

        return _factoryRoom;
    }

Here is a breakdown of what’s happening:

Our TransportMechanism returns a FactoryRoom implementation, based on the RobotPart carried by the WorkerDrone, and then the FactoryRoom adds the TransportationMechanism to its list of occupants:

C#

        public void AddTransportationMechanism(TransportMechanism transportMechanism) {
            _transportMechanisms.Add(transportMechanism);
        }

Java

    public void addTransportationMechanism(transportMechanism transportMechanism) {
        _transportMechanisms.add(transportMechanism);
    }

OK. Now our WorkerDrone has entered the FactoryRoom. It should now offload its RobotParts via the OffLoadRobotParts method above. Here’s what’s happening:

  • A safeguard is in place to ensure that the WorkerDrone enters the room before offloading components
  • The WorkerDrones RobotPart payload is copied to the FactoryRoom
  • The WorkerDrones RobotPart payload is emptied

“Why the safeguard? Can’t we just explicitly call the EnterRoom method before calling OffLoadRobotParts?”

Yes, but let’s offer another layer of protection for consuming applications. After all, if a developer forgot to ensure that a WorkerDrone enters a room before offloading RobotParts, the system would crash. Even if we implemented counter-measures to prevent this, our WorkerDrone would effectively dump its payload somewhere in the Factory.

What do you expect me to do now?!?

What do you expect me to do now?!?

Our WorkerDrone is now housed within an appropriate FactoryRoom, and has offloaded its RobotParts to that FactoryRoom.

“So how did we get here?”

Let’s examine the associated Unit Test:

C#

        [Test]
        public void WorkerDroneOffLoadsRobotParts() {
            WorkerDrone workerDrone = new MockedWorkerDrone();
            RobotPart robotPart = new MockedRobotPart(RobotPartCategory.Assembly);

            workerDrone.PickUpRobotPart(robotPart);
            var factoryRoom = workerDrone.TransportRobotParts();

            Assert.AreEqual(0, workerDrone.GetRobotPartCount());
            Assert.AreEqual(1, factoryRoom.GetRobotPartCount());
            Assert.IsInstanceOf<AssemblyRoom>(factoryRoom);

            robotPart = new MockedRobotPart(RobotPartCategory.Weapon);

            workerDrone.PickUpRobotPart(robotPart);
            factoryRoom = workerDrone.TransportRobotParts();

            Assert.AreEqual(0, workerDrone.GetRobotPartCount());
            Assert.AreEqual(1, factoryRoom.GetRobotPartCount());
            Assert.IsInstanceOf<Armoury>(factoryRoom);
        }

Java

    @Test
    public void workerDroneOffLoadsRobotParts() {
        workerDrone workerDrone = new mockedWorkerDrone();
        robotPart robotPart = new mockedRobotPart(robotPartCategory.assembly);

        workerDrone.pickUpRobotPart(robotPart);
        factoryRoom factoryRoom = workerDrone.transportRobotParts();

        assertEquals(0, workerDrone.getRobotPartCount());
        assertEquals(1, factoryRoom.getRobotPartCount());
        assertThat(factoryRoom, instanceOf(assemblyRoom.class));

        robotPart = new mockedRobotPart(robotPartCategory.weapon);

        workerDrone.pickUpRobotPart(robotPart);
        factoryRoom = workerDrone.transportRobotParts();

        assertEquals(0, workerDrone.getRobotPartCount());
        assertEquals(1, factoryRoom.getRobotPartCount());
        assertThat(factoryRoom, instanceOf(armoury.class));
    }

Notice out first pair of Asserts. We’ve transported the RobotParts from WorkerDrone to FactoryRoom, and simply assert that both components contain the correct number of RobotParts. Next, we assert that our TransportMechanism has selected the correct FactoryRoom instance; Weapons go to the Armoury, Assemblies to the AssemblyRoom.

“Great. I just looked at those FactoryRoom and RobotPart implementations. They’re all implementations of abstractions. Why didn’t you use interfaces, instead of abstract classes?”

There are 2 reasons for this:

  1. Our abstractions contain methods that need to be accessed by the implementations
  2. Our implementations are instances of our abstractions from a real-world perspective – they don’t just exhibit a set of behaviours.

It’s worth noting that a class can derive from a single class only in both C# and Java, whereas a class can derive from many interfaces as you like.

The next tutorial in the series will focus on returning our WorkerDrones to the DeliveryBay, and outlining the structure of RobotBuilders.

Connect with me:

RSSGitHubTwitter
LinkedInYouTubeGoogle+

Object Oriented, Test Driven Design in C# and Java: A Practical Example Part #2

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:

Walker Robot, Vertical

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

White Mech weapon on a white background

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:

  1. WorkerDrone enters DeliveryBay and collects a RobotPart
  2. The RobotPart is identified as an item that belongs in the AssemblyRoom
  3. WorkerDrone transports the RobotPart to the AssemblyRoom
  4. WorkerDrone picks up the next RobotPart, which happens to be a Weapon
  5. 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:

  1. We instnantiate a new RobotPart as an Assembly
  2. We instatiate our WorkerDrone
  3. The WorkerDrone examines the RobotPart and instantiates its TransportationMechanism appropriately
  4. We assert that the correct TransportationMechanism behavioural implementation has been applied for the Assembly RobotPart
  5. 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:

RSSGitHubTwitter
LinkedInYouTubeGoogle+