Comrade Cola – Oppress Your Thirst!
First of all, I had one of the weirdest dreams after reading 1984, World War Z, and the latest from the In Death series. Not in one sitting mind you. As a result, a completely fictitious company was born – The Comrade Cola Company. Orwellian due to its size and influence. Brooksian because there is something sinister lingering beneath the surface. Robbian in its futuristic setting and charismatic cast of characters.
This company will serve as our client, headed by Ms. C, with never-ending changes. Here, I will show my-world examples of how we can use different coding languages to solve them. Let’s play with JavaScript (TypeScript), PHP, C#, Python and Java. The most noteworthy take away here is to use the right tool for the job.
The Chairman of the company wants to become a major player in the food and beverage industry. Therefore, our crew has been “hired” to help make her want a reality. The Chairman expresses her dream of making ice-cold Comrade Cola available to the masses.
Our First Gig!
Sounds like we’ve got some sort of vending machine on our hands. Let’s take a look at how I approached this in the past. It is a common way something like this gets coded.
Code Breakdown
Class Definition
That is a lot of code. Here’s a breakdown of what is going on. First is the start of our class definition. ComradeColaMachine is pretty specific. One may argue it is better practice to abstract that out a bit further to say, SodaMachine. Either way is fine.
<?php
class ComradeColaMachine
{
}
Constants
Our machine will be in one of several modes. These modes, or states, the Comrade Cola machine can be in at any given time are:
- No more cola!
- The machine is waiting for money.
- Customer has put in money.
- The machine has accepted the order.
Most programming languages have a way for you define constants. PHP lets us do so like this.
const _EMPTY = 1;
const _NO_CREDIT = 2;
const _HAS_CREDIT = 3;
const _SOLD = 4;
Certainly there are more states our machine can enter. We’ll actually visit adding another state later on.
Class Properties
We need to keep track of the machine’s current state. The machine will be empty upon delivery, so we start out setting the state variable to _EMPTY, which is static because it is a constant. That is why I am using the self keyword instead of the this keyword. We also need to keep track of how many Comrade Colas are in the machine.
private $state = self::_EMPTY;
private $inventory = 0;
Constructor
The constructor is pretty straight forward. It takes the initial inventory of colas. We’ll put the machine in a state of NO_CREDIT if the inventory is greater than 0. This means the machine is waiting someone to insert money. If not, we’ll leave it at 0.
function __construct($inventory)
{
$this->inventory = $inventory;
if($inventory > 0)
{
$this->state = self::_NO_CREDIT;
}
}
Inserting Credits
Comrade Cola machines need to be able to take credits and make decisions afterwards. Some of you may have started with if/else conditional statements. That is fine, but I want to challenge you to think about another way of checking for a condition. We’ll switch based on the state of the machine.
This is a great time to bring up the topic of requirements. Our team has been given a problem to solve – quenching the thirsts of the masses. Our client has a certain rules our solution needs to follow. These are the business rules and functional requirements we have to follow in order to deliver what the client has asked for. Here are some of the rules we need to implement.
- As a customer I want the machine to only take the amount of credits needed.
- As a customer I want the machine to accept my valid credits.
- As a customer I don’t want the machine to take my credits if it is out of cola.
- As the owner I don’t want the machine to take credits if it is fulfilling an order.
- And we probably ought to handle the unexpected.
function insertCredit()
{
switch($this->state)
{
case self::_HAS_CREDIT:
echo "You can't insert any more credits at this time.\n<br>";
break;
case self::_NO_CREDIT:
$this->state = self::_HAS_CREDIT;
echo "Your credit has been accepted.\n<br>";
break;
case self::_EMPTY:
echo "You can't insert credits because this machine is out of Comrade Cola.\n<br>";
break;
case self::_SOLD:
echo "Please wait. Your cold, refreshing Comrade Cola is being dispensed.\n<br>";
break;
default:
echo "Whoa there comrade! We need to raise some sort of error.\n<br>";
}
}
Returning Credits
In the unfortunate event someone decides to cancel their order, Comrade Cola machines need to be able to return credits. Again, we’ll switch based on the state of the machine. There are more rules we need to be aware of as well.
- As a customer, I want the machine to be able to return my credits.
- As the owner, I want the machine to only return credits for the current transaction.
- As the owner, I want the machine to know not to return credits if none have been used.
- As the owner, I want the machine to know not to return credits if it is sold out.
- As the owner, I want the machine to know not to return credits if an order is being fulfilled.
function returnCredit()
{
switch($this->state)
{
case self::_HAS_CREDIT:
echo "Your credit is being returned.\n<br>";
$this->state = self::_NO_CREDIT;
break;
case self::_NO_CREDIT:
echo "You haven't inserted any credits.\n<br>";
break;
case self::_EMPTY:
echo "There are no credits to return because sold out machines don't accept credits.\n<br>";
break;
case self::_SOLD:
echo "Sorry Comrade. You already bought a cold, refreshing Comrade Cola!\n<br>";
break;
default:
echo "Whoa there comrade! We need to raise some sort of error.\n<br>";
}
}
Getting Closer. Making a Choice.
At the end of the day it is all about choice. At least that is what Ms. C wants us to think. Comrade Cola machines need to be able to take customer’s choice. We have another set of rules to implement.
- As a customer, I want to get my choice of Comrade Cola.
- As the owner, I don’t want the machine to fulfill orders without credits.
- As the owner, I to make sure the customer pays for each Comrade Cola.
function customerChoice()
{
switch($this->state)
{
case self::_HAS_CREDIT:
echo "You've made your choice...\n<br>";
$this->state = self::_SOLD;
$this->dispense();
break;
case self::_NO_CREDIT:
echo "You've made a choice, but haven't inserted any credits.\n<br>";
break;
case self::_EMPTY:
echo "You've made a choice, but we are sold out of Comrade Cola.\n<br>";
break;
case self::_SOLD:
echo "Sorry Comrade. You only get one cold, refreshing Comrade Cola per credit!\n<br>";
break;
default:
echo "Whoa there comrade! We need to raise some sort of error.\n<br>";
}
}
Sweet Oppression! Dispensing the Drink.
Comrade Cola machines need to be able to dispense cold, refreshing Comrade Colas. The best cola of all colas. There is one final rule we need to implement.
- As a customer, I want to get the cola I paid for.
function dispense()
{
switch($this->state)
{
case self::_HAS_CREDIT:
echo "No Comrade Cola yet. You must make a choice first.\n<br>";
break;
case self::_NO_CREDIT:
echo "No Comrade Cola yet. You haven't inserted any credits.\n<br>";
break;
case self::_EMPTY:
echo "No Comrade Cola yet. Sold out machines can't dispense colas.\n<br>";
break;
case self::_SOLD:
echo "Congrats Comrade! Enjoy your cold, refreshing Comrade Cola!\n<br>";
$this->inventory -= 1;
if($this->inventory == 0)
{
echo "The machine is out of Comrade Cola!\n<br>";
$this->state = self::_EMPTY;
}
else
{
$this->state = self::_NO_CREDIT;
}
break;
default:
echo "Whoa there comrade! We need to raise some sort of error.\n";
}
}
A Couple of Cleanup Items
There are a couple of things that will put us in a good position for the next iteration of these soda machines. First is changing the toString() method to print some meaningful messages. This will come in handy for testing and debugging. Please test your code. If you have the luxury of a QA department, test your code before sending it to QA. They are not there to create defects. They are there to validate the software. We could have a huge discussion on that point if you’d like. Second, we may want to give some thought about refilling the machine.
function __toString()
{
$string = sprintf("Comrade Cola PHP Vending Machine.\n<br>Inventory: %d\n<br>", $this->inventory);
if($this->inventory > 0 && $this->state != self::_HAS_CREDIT)
{
$string .= "Machine is waiting for credits.\n<br>";
}
return $string . "\n<br />";
}
function refill()
{
//todo
}
Testing
Let’s test our machine.
$ccMachine = new ComradeColaMachine(5);
echo $ccMachine;
$ccMachine->insertCredit();
echo $ccMachine;
$ccMachine->customerChoice();
echo $ccMachine;
$ccMachine->insertCredit();
echo $ccMachine;
$ccMachine->returnCredit();
echo $ccMachine;
$ccMachine->customerChoice();
echo $ccMachine;
Good. It works! Ice cold Comrade Colas for the whole crew. The Chairwoman is overjoyed with the test run of the software. Production of the machines gets ramped up. And they soon become a success in the field.
This is part of my 100 Days of Code Challenge series posted in Technology. Be sure to check out those posts. I will revisit this code in a new post that deals with a change request that just came in. Didn’t see that coming, did you?