Allows an object to alter its behavior when its internal state changes. The object will appear to change its class.
John works as a customer support representative in a popular telephone support company. In a customer-service-oriented company, the company's priority is to make sure that the customers are satisfied and happy. To improve the service, the company invested a lot of money on improving the support representative’s service. Recent studies show that a support representative’s emotions affect how they perform on the job. John himself admits that his first few words when he answers the phone usually indicates the kind of mood he is in. When he is in a happy mood, he would normally greet the customer with a jolly “Hi there!” or “Hello, How can I help you?” When he is angry and in a dark mood, his answer would be a curt “Hello” or “Yes?” If John is in a moderate mood, he would open the conversation with “Hi” or a mellow “Hello.”
Let's put the above information into code:
class SupportRep
{
private $_state = "happy";
public function setState($state)
{
$this->_state = $state;
}
public function sayHi()
{
if ($this->_state=='happy') {
echo 'hi there!';
} else if ($this->_state=='angry') {
echo 'yes?';
} else if ($this->_state=='moderate') {
echo 'hi';
}
}
}
The code above is pretty straightforward and can help us study the representative’s behavior. However, as research progresses and more information is introduced, the code may not be sufficient. Research show that a person’s mood will change depending on the number of calls he's had and of course his current mood.
When John picks up five to ten phone calls, his mood will change from “happy” to “moderate,” this is probably because he is getting tired or is getting stressed from the calls. If he gets more than 10 phone calls, his mood will change from “moderate” to “angry,” it is indeed quite taxing to be handling too many support calls.
Let us rework our code to add additional logics:
class SupportRep
{
private $_state = null;
public $numOfCalls = 0;
public function setState($state)
{
$this->_state = $state;
}
public function sayHi()
{
if ($this->_state=='happy') {
echo 'hi there!';
if ($this->numOfCalls>5&&$this->numOfCalls<10) {
$this->setState('moderate');
}
if ($this->numOfCalls>10) {
$this->setState('angry');
}
} else if ($this->_state=='angry') {
echo 'yes?';
} else if ($this->_state=='moderate') {
echo 'hi';
if ($this->numOfCalls>10) {
$this->setState('angry');
}
}
$this->numOfCalls;
}
}
Do you see anything wrong with the code above? No, there is nothing wrong with it except that it is cluttered with those if-else statements. It does not even look object-oriented to us, and the worst part is that it clearly violates the single responsibility principle (SRP). There are multiple reasons for this class to change. When you have additional logics for either happy state, moderate state, or angry state. It introduces code modification and remember we are just talking about sayHi() function here. What happens when the mood-based conditions apply to every aspect of a sales representative?
Its time for a change. In this case, we are ready to use the State Pattern. the State Pattern allows us to encapsulate the part that varies, which is the sayHi function. It varies depending on the representative’s mood.
First, let us create a StateInterface, which all subsequent states have to implement:
interface StateInterface
{
public function sayHi();
}
Then we will create three state's classes, and each implements its own logic:
class AngryState implements StateInterface
{
private $_supportRep = null;
public function __construct(SupportRep $supportRep)
{
$this->_supportRep = $supportRep;
}
public function sayHi()
{
echo 'yes?';
}
}
class ModerateState implements StateInterface
{
private $_supportRep = null;
public function __construct(SupportRep $supportRep)
{
$this->_supportRep = $supportRep;
}
public function sayHi() {
echo 'hi';
if ($this->_supportRep->numOfCalls>10) {
$this->_supportRep->setState(new AngryState($this->_supportRep
));
}
}
}
class HappyState implements StateInterface
{
private $_supportRep = null;
public function __construct(SupportRep $supportRep)
{
$this->_supportRep = $supportRep;
}
public function sayHi()
{
echo 'hi there!';
if ($this->_supportRep->numOfCalls>5&&$this->_supportRep->numOfCalls<10) {
Rep));
));
}
}
Finaly, we need to rework SupportRep class.
class SupportRep
{
private $_state = null;
public $numOfCalls = 0;
public function __construct()
{
$this->_state = new ModerateState($this);
}
public function setState($state)
{
$this->_state = $state;
}
public function sayHi()
{
$this->_state->sayHi();
$this->numOfCalls++;
}
}
As you can see, the sayHi() function of SupportRep class delegates its responsibility to state class. It no longer violates SRP. Three states classes are still able to change the main class’s state via composition.
Now SupportRep has become cleaner, and if any additional logic needs to be added, it can be done as an in individual state class. Additional functions of SupportRep can be delegated to states classes.
In our example, the State Pattern Allows an object(Support Rep object) to alter its behavior (sayHi function in individual state class) when its internal state changes (changing between HappyState, ModerateState, and AngryState). The object will appear to change its class.
Hopefully this simple tutorial helped you with your development. If you like our post, please follow us on Twitter and help spread the word. We need your support to continue. If you have questions or find our mistakes in above tutorial, do leave a comment below to let us know.