C++ code for state machine

前端 未结 6 862
暖寄归人
暖寄归人 2020-12-04 05:49

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

6条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-04 06:24

    I was thinking in a more OO approach, using the State Pattern:

    The Machine:

    // 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;
    }
    

    The States:

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

提交回复
热议问题