Sample Chapter

State

This is the 17 Chapter from the book and deals with the state pattern. 

Requirement: How can an object alter its behavior when it’s internal state changes?

Discussion:
1.     What is state? How is state different from the data belonging to an object? Data belongs to an object and are the values of the different variables defined within an object. State can be viewed as those specific values of specific variables depending on which the logic of the methods may change. In this view, all data are not representative of the state of an object. All data are not states. All states are however data within the object.
2.     What is changing? Is it that the object will have new methods if the state changes? Or will the methods of the object remain the same, rather the logic within a method change when the state changes? What is meant here is that the implementation remains the same, so the signature of all methods that were there in the object originally remains as it is, it is the logic within some or all the methods that are changing. We refer to this as the behavior of the method.
3.     Can we envisage a situation where behavior changes based on the state of the object? We do encounter this in common scenarios. An example would be if a bank account is an object, then depending upon whether it is active or dormant we can decide if deposit() and withdrawal() will be supported.
4.     Are we considering the inclusion of new methods? The inclusion of new methods can be done using Visitor pattern, we are not considering that here.
5.     So let us rewrite our requirement as; Given that the methods within an object have different logic depending upon the state of the object the requirement is that if the state of the object changes, the logic with the methods are required to be different.
6.     To be sure of our intent, let us be sure of what is our target design. How shall the ExternalSystem use such an object? To answer this, we consider the ExternalSystem and an objectA which has
a.     A single (single for simplicity) variable (say) varA.  This variable can take values say valueA, valueB … valueS. That is there are S states. It also means the object can be in any one of the above states.
b.     A method (say) named methodA
c.      Now our design should be such that if the value of varA is valueA, the logic within method should be logicA and so on.
7.     Do we have any other additional requirements? Yes, there are few hidden requirements as below
a.     We do not know all the number of states.
b.     Most of the methods within this object behaves differently (i.e., has different logic) depending upon which state the object is in.
c.      The design must work with more number of variables.
8.     Can we now identify the problems this would cause if there are many states and many methods within the object? This point to the cause of this design pattern. We can spot that if s is the number of states and m is the number of methods, then for each of the method dependent on the states we are going to have s number of logic(s) to be applied to each method. So the total number of logic required would be s * m. Also, this would increase as the number of states or methods increase.
9.     To get to the solution, we once again look at how the situation looks like from the ExternalSystem point of view. From the ExternalSystem point of view, it would seem as if there are two objects say objectA  and objectB, objectB being similar to objectA in terms of
a.     The methods they implement,
b.     And the variables
c.      However, it is as if
                                                    i.     objectB has the value of varA as valueB and methodA has logicB as its logic.
While if
                                                  ii.     objectA has the value of varA as valueA and methodA has logicA as its logic.
10.  At this stage for a moment, it looks like as we can have a FactoryClass which can produce these objects depending upon the variety of states. It is not important to know Factory pattern here, what is important is to recognize the ExternalSystem has to create or obtain the FactoryClass. The assumption is we do not want it that way. It is not required that the ExternalSystem create another intermediary to handle this. So the idea is if would be good if objectA itself could behave like objectB or objectC. In that way that the behavior changes without ExternalSystem having to know or to do anything.
11.  Now assuming that ExternalSystem was working with objectA i.e. had a reference to objectA and it had a certain state and its methods behaved in a certain way. How could the ExternalSystem suddenly work with a different object having a different internal state and method behavior? Only if objectA swapped itself with an instance of objectB. ExternalSystem would still have a reference of objectA but the object would be objectB.
a.     When can this swapping be possible?  Only if they derive from the same base class or implement a common interface.
b.     How shall we achieve this swap within the ExternalSystem? If we achieve this swap in the ExternalSystem then the ExternalSystem becomes aware of the swap. Do we want this? No. We do not want the ExternalSystem to know this.
c.      So the situation is we do not want the ExternalSystem to know this and by the design of the language we are using which works with references rather than pointers, the objectA is unable to replace itself using objectB.
12.  So how do we bridge this gap? A way to do this is to use another variable inside our AClass and set it up like this
a.     It has a variable say objectStateVar which can hold objectA or objectB.
b.     It has the same implementation of methods as objectA or objectB.
c.      Based on the state of the state variable it changes the object objectStateVar holds which is either objectA or objectB.
d.     Now when a method which changes its logic AClass delegates the call to the corresponding method to objectStateVar.
e.     In this way, the ExternalSystem does not know that the actual object handling its request has changed.
13.  When should this swap occur? Our idea here is to replace object-variable within the AClass when the state changes. When this would happen depends upon the context.  From a program point of view, this would be some point in the execution of some method with the AClass. A likely place for this transition can be the setter method of varA within objectStateContext.
14.  What should be the logic within the setter method of varA within objectStateContext? It should first set the variable varA. Next it assigns the subsequent state object to objectStateVar.
15.  What would be the subsequent state object? This would depend on the state transition diagram. If in the state transition
a.     One state leads to only one another state then the assignment is simple. For example, if there are two states then there would be two state classes and the objectStateVariable would be alternatively set to aObject and bObject. This is the case of an on-off button.
b.     If one state can lead to more than one other states then an if-else loop would be required to decide the next state object. 

Participants:
1.     ExternalSystem – This is our client class.
2.     AClass - This is the class that the ExternalSystem will use which will appear to change the logic of its method based on the state of one of its variable called the state variable.
3.     A1Class, A2Class – The classes which have the same implementation but different behavior of its methods.
4.     StateInterface – The interface that A1Class, A2Class implement

Steps:
1.     For the given context and for a given class (say) AClass, identify that variable which constitute the state and those methods which change their logic based on the values of this state variable. Find how many such values and logic are there. Suppose the number of states this variable can be in is s and the number of methods which change their logic is m.
2.     Create an interface StateInterface which has only those m methods.
3.     Now for each of the s cases, create a class which has the following
a.     Extend it from AClass. Name it appropriately. We call these as state class.
b.     Make the state variable final, so that the state variable of this object cannot be changed. 
c.      Initialize the state variable to one of the valid state values.
d.     Make it implement the StateInterface
e.     Change the logic within the methods corresponding to this state. So these methods would have the same signature but different logic within them and this is how the state classes differ.
f.      Remove the non-state variables and remaining methods that are not state dependent as all those will be available from the base class.
4.     Now in the AClass
a.     Create a private variable to hold an object of type StateInterface.
b.     Create a setter method for the StateInterface variable. There is no need of getter method for this variable.
5.     Do the swapping or reassignment within AClass
a.     Within the setter method for the state variable
                                                    i.     Set the value of the state variable with the incoming value.
                                                  ii.     Based on the incoming value and the state transition map decide if necessary with an if – else logic as to which state class would be the subsequent state and then set the StateInterface variable in 4.b to that state class.
b.     Within the methods that change, delegate the call to the methods of the StateInterface variable.
6.     The ExternalSystem creates or obtains a reference of AClass and uses it. 
a.     The external system changes the value of the state variable of the aObject.
b.     Depending on the value the methods would have different behavior or logic and that is the intention.

Example code:
{project.root}/com/behavioral/state/

Further notes:
1.     Does there need to be only on variable based on which the state is decided?
2.     Should the ExternalSystem use the StateContextClass directly or should it use only the StateInterface?
3.     Does a number of variables matter? It does because defining the states takes into account the number of variables, their combinations and their values.

4.     Note that varA is most likely to be a discrete variable.

No comments:

Post a Comment