Ways To Improve The Comrade Cola Machine

Comrade Cola

There are several ways to improve the comrade cola machine. If only we had a reason to revisit the code.

New Functionality

The Chairwoman wants to add some new functionality to the system. Say…10% chance to get a 2 for 1. Didn’t see that one coming, did you? I bet you knew it was going to happen. Especially if you read the previous post. We could update the class we have now with the functionality, but that would make the software even more inflexible.

Let’s see how we can fix this up with sound OO principals. Mainly encapsulation so our code is as flexible and decoupled as possible. Let us also take a look at the State design pattern while we’re at it. These are a few of the ways to improve our code base.

State Pattern

I mentioned earlier that we would look for ways to improve our code. Up first – The state pattern, which closely resembles Strategy Pattern, is a behavioral software design pattern, also known as the objects for states pattern. This pattern is used in computer programming to encapsulate varying behavior for the same routine based on an object’s state object. This can be a cleaner way for an object to change its behavior at runtime without resorting to large monolithic conditional statements.

Check out the entry for State_pattern at Wikipedia for more info. http://en.wikipedia.org/wiki/State_pattern

We did a lot of not-so-best practices in the first version of the Chairman’s cola machine. Hopefully she does not find out. Decision making for each machine state was handled by the action. What if we turned that logic around so that the different machine states would be able to act on their own?

First we need to figure out which states our machine will need to be aware of. Our earlier case statements give us the information we’re looking for.

switch($this->state) 
{
   case self::HAS_CREDIT:
   case self::NO_CREDIT:
   case self::EMPTY:
   case self::SOLD:
}

Each of those cases should be a State class. And each of those classes should implement an interface that defines the actions that can be taken by the machine.

We’ll gain encapsulation by having each class perform class related tasks. This also helps with maintenance, by allowing us to add new functionality without rewriting major blocks of code.

State Interface

Let’s define that interface that all machine states (HAS_CREDIT, SOLD, etc) will need to implement. Think of this interface as a binding contract that all classes wishing to use it, have agreed to implement the methods.

interface StateInterface
{
  public function insertCredit();
  public function returnCredit();
  public function customerChoice();
  public function dispense();
}

Each of the State classes will implement the StateInterface. Here’s our first machine state that implements the new interface. Each state will be have a Comrade Cola machine passed to it. Notice that I have a private instance variable that will be set at construction time. We will also set ourselves up to account for the new functionality here.

The other states are fairly similar to the previous methods.

  • NoCreditState.php
  • EmptyState.php
  • SoldState.php

Promotional Update

That puts us at parity with the previous version of the Comrade Cola machine. We have made our code more manageable by breaking up that huge block of code into separate classes. Easier to read. Easier to debug. I call that a win-win. Let’s not forget to add the new functionality the Chairman wants.

Sure, we could have gone into each of the methods in the previous code and bolted on the promotional state in case statements. However, we are keeping it simple by creating another class that agrees to stick to the StateInterface contract.

New and Improved

The constructor takes the initial inventory of colas. The machine enters a state of NO_MONEY if the inventory is greater than 0. This means the machine is waiting for someone to insert money.

You will notice we are taking advantage of the built in PHP magic methods __get() and __set() so we have access to those protected properties.

https://www.php.net/manual/en/language.oop5.overloading.php#object.get

https://www.php.net/manual/en/language.oop5.overloading.php#object.set

public function __get($property) {
    if (property_exists($this, $property)) {
      return $this->$property;
    }
  }

  public function __set($property, $value) {
    if (property_exists($this, $property)) {
      $this->$property = $value;
    }
    return $this;
  }

Testing It Out

Let’s test this thing out!

improved comrade cola machine
Partial results from the test

Looks like everything works. I should really let the Chairwoman know that she has a requirements gap with refilling the vending…Comrade Cola machines. I’ll see you guys here next time.

Day 9 : July 20, 2019 Monday

Today’s Progress:

  1. Broke up the big class into more manageable parts.

Thoughts
Going through this refactor was a blast. Also had fun with learning more about the state design pattern. What’s next?

Link(s) to work

1.CCM – Comrade Cola

You May Also Like