Tag Archives: object oriented design

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+

Microservices in C# Part 5: Autoscaling

Fork me on GitHub

Balancing demand and processing power

Balancing demand and processing power

Autoscaling Microservices

In the previous tutorial, we demonstrated the throughput increase by invoking multiple instances of SimpleMathMicroservice, in order to facilitate a greater number of concurrent inbound HTTP requests. We experimented with various configurations, increasing the count of simultaneously running instances of SimpleMathMicroservice until the law of diminishing returns set it.

This is a perfectly adequate configuration for applications that absorb a consistent number of inbound HTTP requests over any given extended period of time. Most web applications, of course, do not adhere to this model. Instead, traffic tends to fluctuate, depending on several factors, not least of which is the type of business that the web application facilitates.

This presents a significant problem, in that we cannot manually throttle the number of concurrently running Microservice instances on-demand, as traffic dictates. We need an automated mechanism to scale our Microservice instances adequately.

Autoscaling involves more than simply increasing the count of running instances during heavy load. It also involves the graceful termination of superfluous instances, or instances that are no longer necessary to meet the demands of the application as load is reduced. Daishi.AMQP provides just such features, which we’ll cover in detail.

QueueWatch

QueueWatch is a mechanism that allows the monitoring of RabbitMQ Queues in real time. It achieves this by polling the RabbitMQ Management API (mentioned in Part #3) at regular intervals, returning metadata that describes the current state of each Queue.

Metadata

RabbitMQ exposes important metadata pertaining to each Queue. This metadata is presented in a user-friendly manner in the RabbitMQ Management Console:

Message Rates

Message Rates

These metrics represent the rates at which messages are processed by RabbitMQ. “Publish” illustrates the rate at which messages are introduced to the server, while “Deliver” represents the rate at which messages are dispatched to listening consumers (Microservices, in our case).

This information is readily available in the RabbitMQ Management API. QueueWatch effectively harvests this information, comparing the values retrieved in the latest poll with those retrieved in the previous, to monitor the flow of messages through RabbitMQ. QueueWatch can determine whether or not any given Queue is idling, overworked, or somewhere in between.

Once a Queue is determined to be under heavy load, QueueWatch triggers an event, and dispatches an AutoScale message to the Microservice consuming the heavily-laden Queue. The Microservice can then instantiate more AMQPConsumer instances in order to drain the Queue sufficiently.

Just Show Me the Code

Create a new Microservice instance called QueueWatchMicroservice; an implementation of Microservice, and add the following code to the Init method:

            var amqpQueueMetricsManager = new RabbitMQQueueMetricsManager(false, "localhost", 15672, "paul", "password");

            AMQPQueueMetricsAnalyser amqpQueueMetricsAnalyser = new RabbitMQQueueMetricsAnalyser(
                new ConsumerUtilisationTooLowAMQPQueueMetricAnalyser(
                    new ConsumptionRateIncreasedAMQPQueueMetricAnalyser(
                        new DispatchRateDecreasedAMQPQueueMetricAnalyser(
                            new QueueLengthIncreasedAMQPQueueMetricAnalyser(
                                new ConsumptionRateDecreasedAMQPQueueMetricAnalyser(
                                    new StableAMQPQueueMetricAnalyser()))))), 20);

            AMQPConsumerNotifier amqpConsumerNotifier = new RabbitMQConsumerNotifier(RabbitMQAdapter.Instance, "monitor");
            RabbitMQAdapter.Instance.Init("localhost", 5672, "paul", "password", 50);

            _queueWatch = new QueueWatch(amqpQueueMetricsManager, amqpQueueMetricsAnalyser, amqpConsumerNotifier, 5000);
            _queueWatch.AMQPQueueMetricsAnalysed += QueueWatchOnAMQPQueueMetricsAnalysed;

            _queueWatch.StartAsync();

There’s a lot to talk about here. Firstly, remember that the primary function of QueueWatch is to poll the RabbitMQ Management API. In doing so, QueueWatch returns several metrics pertaining to each Queue. We need to decide which metrics we are interested in.

Metrics are represented by implementations of AMQPQueueMetricAnalyser, and chained together as per the Chain of Responsibility Design Pattern. Each link in the chain is executed until a predefined performance condition is met. For example, let’s consider the ConsumerUtilisationTooLowAMQPQueueMetricAnalyser. This implementation of AMQPQueueMetricAnalyser inspects the ConsumerUtilisation metric, and determines whether the value is less than 99%, in which case, there are not enough consuming Microservices to adequately drain the Queue. At this point, a ConsumerUtilisationTooLow value is returned, the chain of execution ends, and QueueWatch issues an AutoScale directive:

        public override void Analyse(AMQPQueueMetric current, AMQPQueueMetric previous, ConcurrentBag<AMQPQueueMetric> busyQueues, ConcurrentBag<AMQPQueueMetric> quietQueues, int percentageDifference) {
            if (current.ConsumerUtilisation >= 0 && current.ConsumerUtilisation < 99) {
                current.AMQPQueueMetricAnalysisResult = AMQPQueueMetricAnalysisResult.ConsumerUtilisationTooLow;
                busyQueues.Add(current);
            }
            else analyser.Analyse(current, previous, busyQueues, quietQueues, percentageDifference);
        }

Scale-Out Directive

Scaling out

Scaling out

QueueWatch must issue Scale-Out directives through dedicated Queues in order to adhere to the Decoupled Middleware design. QueueWatch should not know anything about the downstream Microservices, and should instead communicate through AMQP, specifically, through a dedicated Exchange.

Each Microservice must now listen to 2 Queues. E.g., SimpleMathMicroservice will continue listening to the Math Queue, as well as a Queue called AutoScale, for the purpose of demonstration. SimpleMathMicroservice will receive Scale-Out directives through this Queue. We should modify SimpleMathMicroservice accordingly:

        public void Init() {
            _adapter = RabbitMQAdapter.Instance;
            _adapter.Init("localhost", 5672, "guest", "guest", 50);

            _rabbitMQConsumerCatchAll = new RabbitMQConsumerCatchAll("Math", 10);
            _rabbitMQConsumerCatchAll.MessageReceived += OnMessageReceived;

            _autoScaleConsumerCatchAll = new RabbitMQConsumerCatchAll("AutoScale", 10);
            _autoScaleConsumerCatchAll.MessageReceived += _autoScaleConsumerCatchAll_MessageReceived;

            _consumers.Add(_rabbitMQConsumerCatchAll);

            _adapter.Connect();
            _adapter.ConsumeAsync(_autoScaleConsumerCatchAll);
            _adapter.ConsumeAsync(_rabbitMQConsumerCatchAll);
        }

Create a Topic Exchange called “monitor”. QueueWatch will publish to this Exchange, which will route the message to an appropriate Queue. Now create a binding between the monitor Exchange and the AutoScale Queue:

Exchange Binding

Exchange Binding

Note that the Routing Key is the name of the Queue under monitor. If QueueWatch determines that the Math Queue is under load, then it will issue a Scale-Out directive to the monitor Exchange, with a Routing Key of “Math”. The monitor Exchange will react by routing the Scale-Out directive to the AutoScale Queue, to which an explicit binding exists. SimpleMathMicroservice consumes the Scale-Out directive and reacts appropriately, by instantiating a new AMQPConsumer:

            if (e.Message.Contains("scale-out")) {
                var consumer = new RabbitMQConsumerCatchAll("Math", 10);
                _adapter.ConsumeAsync(consumer);
                _consumers.Add(consumer);
            }
            else {
                if (_consumers.Count <= 1) return;
                var lastConsumer = _consumers[_consumers.Count - 1];

                _adapter.StopConsumingAsync(lastConsumer);
                _consumers.RemoveAt(_consumers.Count - 1);
            }

Summary

QueueWatch provides a means of returning key RabbitMQ Queue metrics at regular intervals, in order to determine whether demand, in terms of the number of running Microservice instances, is waxing or waning. QueueWatch also provides a means of reacting to such events, by publishing AutoScale notifications to downstream Microservices, so that they can scale accordingly, providing sufficient processing power at any given instant. The process is simplified as follows:

  1. QueueWatch returns metrics describing each Queue
  2. Queue metrics are compared against the last batch returned by QueueWatch
  3. AutoScale messages are dispatched to a Monitor Exchange
  4. AutoScale messages are routed to the appropriate Queue
  5. AutoScale messages are consumed by the intended Microservices
  6. Microservices scale appropriately, based on the AutoScale message

Next Steps

  • Prevent a “bounce” effect as traffic arbitrarily fluctuates for reasons not pertaining to application usage, such as network slow-down, or hardware failure
  • The current implementation compares metrics in a very simple fashion. Future implementations will instead graph metric metadata, and react to more thoroughly defined thresholds

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 #4

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…

Oh, no. Something's gone wrong!

“Oh, no. Something’s gone horribly wrong!”

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.

More than 1 RobotPart at a time? Please! Easy work for me!!!

More than 1 RobotPart at a time? Please! Easy work for me!!!

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.

I'm ready to transport more RobotParts! Back to the DeliveryBay I go...

“I’m ready to transport more RobotParts! Back to the DeliveryBay I go…”

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:

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&lt;E extends factoryRoom, U extends robotPart&gt; {

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&lt;armoury, weapon&gt; {

    @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&lt;RobotPart&gt; robotParts) {
            if (_factoryRoom == null) {
                EnterRoom();
            }
            _factoryRoom.SetRobotParts(new List&lt;RobotPart&gt;(robotParts));
            robotParts.Clear();

            return _factoryRoom;
        }

Java

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

    public E offLoadRobotParts(List&lt;U&gt;robotParts) {
        if (_factoryRoom == null) {
            enterRoom();
        }
        _factoryRoom.setRobotParts(new ArrayList&lt;U&gt;(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&lt;AssemblyRoom&gt;(factoryRoom);

            robotPart = new MockedRobotPart(RobotPartCategory.Weapon);

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

            Assert.AreEqual(0, workerDrone.GetRobotPartCount());
            Assert.AreEqual(1, factoryRoom.GetRobotPartCount());
            Assert.IsInstanceOf&lt;Armoury&gt;(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+

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

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.

At this point, many tutorials start by launching into a “Hello, World” style tutorial, with very little practical basis.

HelloWorld

This isn’t the most exciting concept, so let’s try a more practical example. Instead of churning out boring pleasantries, our application is going to do something a bit more interesting…build robots.

Robot

Specifically, our application is going to build awesome robots with big guns.

Ok, let’s get started with a narrative description of what we’re going to do.

“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.

The first thing we’ll do is look at the narrative again, this time highlighting each noun.

“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 the 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 weapon assemblies. 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.

Each highlight represents an actual object that will exist in our simulation.

“But where do I start with all of this?”

I recommend starting with a low-level component; that is, a component with little or no dependency on other objects. Supplier looks like a good place to start. It doesn’t do much, other than deliver RobotParts to a DeliveryBay, so let’s start with that.

I’m assuming at this point, if you’re working with .NET, that you have a new Visual Studio solution and have created a Class Library project including NUnit as an included package. NUnit is the testing framework that we will use to validate our core components.

Or,if you’re working with Java, that you’ve set up a new Java 8 project in your IDE of choice, and have configured JUnit, the testing framework that we will use to validate our core components.

Add a class called RobotPartSupplierTests and add the following method:

C#

        [Test]
        public void RobotPartSupplierDeliversRobotParts() {
            var mechSupplier = new RobotPartSupplier {
                RobotParts = new List<RobotPart> {
                    new MockedRobotPart(),
                    new MockedRobotPart()
                }
            };

            var deliveryBay = new MockedDeliveryBay();
            mechSupplier.DeliverRobotParts(deliveryBay);

            Assert.AreEqual(2, deliveryBay.RobotParts.Count);
            Assert.AreEqual(0, mechSupplier.RobotParts.Count);
        }

Java

    @Test
    public void supplierDeliversRobotParts() {
        robotPartSupplier robotPartSupplier = new robotPartSupplier();
        List<robotPart> robotParts = new ArrayList<robotPart>();

        robotParts.add(new mockedRobotPart());
        robotParts.add(new mockedRobotPart());

        robotPartSupplier.setRobotPart(robotParts);

        deliveryBay mockedDeliveryBay = new mockedDeliveryBay();
        robotPartSupplier.deliverRobotParts(mockedDeliveryBay);

        assertEquals(2, mockedDeliveryBay.getRobotParts().size());
        assertEquals(0, robotPartSupplier.getRobotParts().size());
    }

Here, we declare an instance of RobotPartSupplier, an implementation of the Supplier abstract class:

C#

 public abstract class Supplier {
        public List<RobotPart> RobotParts { get; set; }
    }

Java

public abstract class supplier {
    private List<robotPart> _robotParts;

    public List<robotPart> getRobotParts() {
        return _robotParts;
    }

    public void setRobotPart(List<robotPart> value) {
        _robotParts = value;
    }
}

“Why create an abstraction? Can’t we just deal directly with the concrete implementation?”

Yes, we can. We’ve abstracted to the Supplier class because at some point, we’re going to have to test a component that has a dependency on a RobotPartSupplier. The point of each test in a Test Driven project is that it tests exactly 1 unit of work, and no more. In our case, we’re testing to ensure that a RobotPartSupplier can deliver RobotParts to a DeliveryBay. But if you follow the logic through, we’re also testing concrete implementations of DeliveryBay and RobotPart. This can have negative consequences later on.

What would happen, for example, if we were to change the underlying behaviour of DeliveryBay? For starters, it would break associated DeliveryBay tests.

“OK, so what?”

That’s fine – that’s what the tests are there for. But, it will also break our RobotPartSupplier tests. The point is to isolate each problem domain into a set of tests, and to apply boundaries to those tests such that changes in other problem domains do not impact.
This might sound a bit pedantic. If so, bear with me. It actually provides a level of neatness to our code, which is particularly helpful as codebases grow larger.

Essentially, the rule of thumb is to abstract everything.

Notice that our RobotPartSupplier contains a List of RobotPart. Based on the above concept, The actual RobotPart instances that are loaded in this test are mocked instance. These are dummy implementations of the core abstraction, RobotPart, that will never make it to our actual core components.

“Why bother with them then?”

They provide simple implementations of dependent classes in order to protect our RobotSupplier tests from changes in concrete implementations of those dependent classes.

As you can see, we’ve mocked DeliveryBay and RobotPart abstractions.
Let’s carry on. The first thing that we want to prove here is that our RobotPartSupplier can deliver RobotParts to a DeliveryBay, so our test requires us to implement the DeliverRobotParts method:

C#

    public class RobotPartSupplier : Supplier {
        public void DeliverRobotParts(DeliveryBay deliveryBay) {
            deliveryBay.RobotParts = new List<RobotPart>(RobotParts);
            RobotParts.Clear();
        }
    }

Java

    public void deliverRobotParts(deliveryBay deliveryBay) {
        deliveryBay.setRobotPart(new ArrayList<robotPart>(this.getRobotParts()));
        getRobotParts().clear();
    }

Notice that two things happen here:

  • The RobotPartSupplier’s RobotParts are copied to the DeliveryBay
  • The RobotPartSupplier’s RobotParts are emptied

As per the brief, this concludes our delivery-related functionality. Our test’s assertions determine that the RobotParts have indeed been copied from our RobotPartSupplier to our mocked DeliveryBay.

In next week’s tutorial, we’ll continue our implementation, focusing on WorkerDrones, and their functionality.

Connect with me:

RSSGitHubTwitter
LinkedInYouTubeGoogle+