This was an interview question to be coded in C++:
Write code for a vending machine: Start with a simple one where it just vends one type of item. So
I was thinking in a more OO approach, using the State Pattern:
// machine.h
#pragma once
#include "MachineStates.h"
class AbstractState;
class Machine {
friend class AbstractState;
public:
Machine(unsigned int inStockQuantity);
void sell(unsigned int quantity);
void refill(unsigned int quantity);
unsigned int getCurrentStock();
~Machine();
private:
unsigned int mStockQuantity;
AbstractState* mState;
};
// machine.cpp
#include "Machine.h"
Machine::Machine(unsigned int inStockQuantity) :
mStockQuantity(inStockQuantity),
mState(inStockQuantity > 0 ? new Normal() : new SoldOut()) {
}
Machine::~Machine() {
delete mState;
}
void Machine::sell(unsigned int quantity) {
mState->sell(*this, quantity);
}
void Machine::refill(unsigned int quantity) {
mState->refill(*this, quantity);
}
unsigned int Machine::getCurrentStock() {
return mStockQuantity;
}
// MachineStates.h
#pragma once
#include "Machine.h"
#include
#include
class Machine;
class AbstractState {
public:
virtual void sell(Machine& machine, unsigned int quantity) = 0;
virtual void refill(Machine& machine, unsigned int quantity) = 0;
virtual ~AbstractState();
protected:
void setState(Machine& machine, AbstractState* st);
void updateStock(Machine& machine, unsigned int quantity);
};
class Normal : public AbstractState {
public:
virtual void sell(Machine& machine, unsigned int quantity);
virtual void refill(Machine& machine, unsigned int quantity);
virtual ~Normal();
};
class SoldOut : public AbstractState {
public:
virtual void sell(Machine& machine, unsigned int quantity);
virtual void refill(Machine& machine, unsigned int quantity);
virtual ~SoldOut();
};
// MachineStates.cpp
#include "MachineStates.h"
AbstractState::~AbstractState() {
}
void AbstractState::setState(Machine& machine, AbstractState* state) {
AbstractState* aux = machine.mState;
machine.mState = state;
delete aux;
}
void AbstractState::updateStock(Machine& machine, unsigned int quantity) {
machine.mStockQuantity = quantity;
}
Normal::~Normal() {
}
void Normal::sell(Machine& machine, unsigned int quantity) {
int currStock = machine.getCurrentStock();
if (currStock < quantity) {
throw std::runtime_error("Not enough stock");
}
updateStock(machine, currStock - quantity);
if (machine.getCurrentStock() == 0) {
setState(machine, new SoldOut());
}
}
void Normal::refill(Machine& machine, unsigned int quantity) {
int currStock = machine.getCurrentStock();
updateStock(machine, currStock + quantity);
}
SoldOut::~SoldOut() {
}
void SoldOut::sell(Machine& machine, unsigned int quantity) {
throw std::runtime_error("Sold out!");
}
void SoldOut::refill(Machine& machine, unsigned int quantity) {
updateStock(machine, quantity);
setState(machine, new Normal());
}
I'm not used to program in C++, but this code aparently compiles against GCC 4.8.2 and valgrind shows no leaks, so I guess it's fine. I'm not computing money, but I don't need this to show you the idea.
To test it:
#include
#include
#include "Machine.h"
#include "MachineStates.h"
int main() {
Machine m(10), m2(0);
m.sell(10);
std::cout << "m: " << "Sold 10 items" << std::endl;
try {
m.sell(1);
} catch (std::exception& e) {
std::cerr << "m: " << e.what() << std::endl;
}
m.refill(20);
std::cout << "m: " << "Refilled 20 items" << std::endl;
m.sell(10);
std::cout << "m: " << "Sold 10 items" << std::endl;
std::cout << "m: " << "Remaining " << m.getCurrentStock() << " items" << std::endl;
m.sell(5);
std::cout << "m: " << "Sold 5 items" << std::endl;
std::cout << "m: " << "Remaining " << m.getCurrentStock() << " items" << std::endl;
try {
m.sell(10);
} catch (std::exception& e) {
std::cerr << "m: " << e.what() << std::endl;
}
try {
m2.sell(1);
} catch (std::exception& e) {
std::cerr << "m2: " << e.what() << std::endl;
}
return 0;
}
Output is:
m: Sold 10 items m: Sold out! m: Refilled 20 items m: Sold 10 items m: Remaining 10 items m: Sold 5 items m: Remaining 5 items m: Not enough stock m2: Not enough stock
Now, if you want to add a Broken
state, all you need is another AbstractState
child. Maybe you'll need to add a broken
property on Machine
also.
To add more products, you must have a map of products and its respective in-stock quantity and so on...