design_pattern_state

This post implements state design patter in <Head First Design Pattern>.  The state pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class. In plain words, when the run-time behavior depends on state and state will change according to behavior at run-time, perhaps we need to consider state pattern.


The State pattern is a solution to the problem of how to make behavior depend on state.

  • Define a "context" class to present a single interface to the outside world.
  • Define a State abstract base class.
  • Represent the different "states" of the state machine as derived classes of the State base class.
  • Define state-specific behavior in the appropriate State derived classes.
  • Maintain a pointer to the current "state" in the "context" class.
  • To change the state of the state machine, change the current "state"pointer.

The State pattern does not specify where the state transitions will be defined. The choices are two: the "context" object, or each individual State derived class. The advantage of the latter option is ease of adding new State derived classes. The disadvantage is each State derived class has knowledge of (coupling to) its siblings, which introduces dependencies between subclasses.

To implement the design, we need to

  1. Identify an existing class, or create a new class, that will serve as the "state machine" from the client's perspective.That class is the "wrapper" class.
  2. Create a State base class that replicates the methods of the state machine interface. Each method takes one additional parameter:an instance of the wrapper class. The State base class specifies any useful "default" behavior.
  3. Create a State derived class for each domain state. These derived classes only override the methods they need to override.
  4. The wrapper class maintains a "current" State object.
  5. All client requests to the wrapper class are simply delegated to the current State object, and the wrapper object'sthis pointer is passed.
  6. The State methods change the "current" state in the wrapper object as appropriate
The example in the book is 

First of all, we define an abstract interface for state

class State {
public:
    virtual void insertQuarter() = 0;
    virtual void ejectQuarter() = 0;
    virtual void turnCrank() = 0;
    virtual void dispense() = 0;
    virtual void toString() = 0;
};
Such abstract class does nothing but defines an uniform interface for states via several pure virtual functions. Then we further implement such interface to define concrete states. We look at HasQuarterState

class HasQuarterState : public State {
private:
    GumballMachine* gumballMachine;

public:
    HasQuarterState(GumballMachine* gumballMachine) {
		this->gumballMachine = gumballMachine;
	}
	virtual void insertQuarter() {
		std::cout << "You can't insert another quarter" << std::endl;
	}
	virtual void ejectQuarter() {
		std::cout << "Quarter returned" << std::endl;
		gumballMachine->setState(gumballMachine->getNoQuarterState());
	}
    virtual void turnCrank() {
		std::cout << "You turned..." << std::endl;
		gumballMachine->setState(gumballMachine->getSoldState());
	}
    virtual void dispense() {
        std::cout << "No gumball dispensed" << std::endl;
    }
	virtual void toString() {
		std::cout << "waiting for turn of crank" << std::endl;
	}
};
HasQuarterState inherits State. It has a private data member, which points to our gumballMachine. This is because in the state, during conducting certain move, we may change state of gumballMachine. Because we have quarter in the machine, we cannot insertQuarter(), however, we could ejectQuarter(). Moreover, we could use the quarter to buy gumballs, turnCrank. If you do nothing, it will print "waiting for turn of crank". The other states are

class NoQuarterState : public State {
private:
    GumballMachine* gumballMachine;

public:
    NoQuarterState(GumballMachine* gumballMachine) {
        this->gumballMachine = gumballMachine;
    }
	virtual void insertQuarter() {
		std::cout << "You inserted a quarter" << std::endl;
		gumballMachine->setState(gumballMachine->getHasQuarterState());
	}
	virtual void ejectQuarter() {
		std::cout << "You haven't inserted a quarter" << std::endl;
	}
	virtual void turnCrank() {
		std::cout << "You turned, but there's no quarter" << std::endl;
	}
	virtual void dispense() {
		std::cout << "You need to pay first" << std::endl;
	}
    virtual void toString() {
		std::cout << "waiting for quarter" << std:endl;
	}
};

class SoldOutState : public State {
private:
    GumballMachine* gumballMachine;

public:
    SoldOutState(GumballMachine* gumballMachine) {
        this->gumballMachine = gumballMachine;
    }
	virtual void insertQuarter() {
		std::cout << "You can't insert a quarter, the machine is sold out" << std::endl;
	}
	virtual void ejectQuarter() {
		std::cout << "You can't eject, you haven't inserted a quarter yet" << std::endl;
	}
	virtual void turnCrank() {
		std::cout << "You turned, but there are no gumballs" << std::endl;
	}
	virtual void dispense() {
		std::cout << "No gumball dispensed" << std::endl;
	}
	virtual void toString() {
		std::cout << "sold out" << std::endl;
	}
};

class SoldState : public State {
private:
    GumballMachine* gumballMachine;

public:
    SoldState(GumballMachine* gumballMachine) {
        this->gumballMachine = gumballMachine;
    }
	virtual void insertQuarter() {
		std::cout << "Please wait, we're already giving you a gumball" << std::endl;
	}
	virtual void ejectQuarter() {
		std::cout << "Sorry, you already turned the crank" << std::endl;
	}
    virtual void turnCrank() {
		std::cout << "Turning twice doesn't get you another gumball!" << std::endl;
	}
	virtual void dispense() {
		gumballMachine->releaseBall();
		if (gumballMachine->getCount() > 0) {
			gumballMachine->setState(gumballMachine->getNoQuarterState());
		} else {
			std::cout << "Oops, out of gumballs!" << std::endl;
			gumballMachine->setState(gumballMachine->getSoldOutState());
		}
	}
	virtual void toString() {
		std::cout << "dispensing a gumball" << std::endl;
	}
};
The GumballMachine  class is given by

#ifndef GUMBALLMACHINE_HPP_INCLUDED
#define GUMBALLMACHINE_HPP_INCLUDED

class GumballMachine {
private:
    State* soldOutState;
	State* noQuarterState;
	State* hasQuarterState;
	State* soldState;

	State* state;	// = soldOutState;
	int count;		// = 0;

public:
    GumballMachine(int numberGumballs) {
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        winnerState = new WinnerState(this);

        this->state = soldOutState;
        this->count = numberGumballs;
        if (numberGumballs > 0) {
            state = noQuarterState;
        }
    }
	virtual void insertQuarter() {
		state->insertQuarter();
	}
	virtual void ejectQuarter() {
		state->ejectQuarter();
	}
	virtual void turnCrank() {
		state->turnCrank();
		state->dispense();
	}
	virtual void setState(State* state) {
		this->state = state;
	}
	virtual void releaseBall() {
		std::cout << "A gumball comes rolling out the slot..." << std::endl;
		if (count != 0) {
			count = count - 1;
		}
	}
	virtual int getCount() { return count; }
	irtual void refill(int count) {
		this->count = count;
		state = noQuarterState;
	}
    virtual State* getState() { return state; }
    virtual State* getSoldOutState() { return soldOutState; }
    virtual State* getNoQuarterState() { return noQuarterState; }
    virtual State* getHasQuarterState() { return hasQuarterState; }
    virtual State* getSoldState() { return soldState; }
	virtual void toString() {
		std::cout << std::endl << "Mighty Gumball, Inc.";
		std::cout << std::endl << "C++-enabled Standing Gumball Model #2004";
		std::cout << std::endl << "Inventory: " << count << " gumball";
		if (count != 1) {
			std::cout << "s";
		}
		std::cout << std::endl;
		std::cout << "Machine is " << state->toString() << std::endl;
	}
};

#endif // GUMBALLMACHINE_HPP_INCLUDED

The main function

int main() {

	GumballMachine* gumballMachine = new GumballMachine(10);

	std::cout << gumballMachine->toString() << std::endl;

	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();
	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();

	std::cout << gumballMachine->toString() << std::endl;

	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();
	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();

	std::cout << gumballMachine->toString() << std::endl;

	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();
	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();

	std::cout << gumballMachine->toString() << std::endl;

	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();
	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();

	std::cout << gumballMachine->toString() << std::endl;

	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();
	gumballMachine->insertQuarter();
	gumballMachine->turnCrank();

	std::cout << gumballMachine->toString() << std::endl;

	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值