State Design Pattern using java .. A different approach !

"The state pattern is a behavioral software design pattern, also known as the objects for states pattern. This pattern is used in computer programming to represent the state of an object. This is a clean way for an object to partially change its type at runtime.

Source : Wikipedia "

Cabin crew's safety announcements are about to kick off. Beside me, on the aisle seat, a girl who just switched on her tabletpc (hp 2710p model!). She seems to be writing some Java stuff on netbeans and her(!) xwindows' look and feel is very nice which prompted me to ask what it is.

"Its the latest opensolaris mate "

"Oh lovely, I meant your GNOME aesthetics, BTW I am JK, May I know your good name please ?"

" Renee Zellweger !!!" (NB : If you are caught up with Jerry Maguire heroine, its not my mistake!!! )

"Oh ok, I see you writing some Java stuff. How is it going ?"

"Well I am trying to implement the State pattern in the conventional way. but I am not convinced with the ways its going. It should be much much better that what this wiki entry is talking about it"

"Oh dear, You are really spot on with.... The common mistakes with these       wiki entries are they just address raw pattern models, but they never addresses the best possible practical use of it.. If you don't mind lets discuss this together"

"Well yeah, it's a seven hours flight, who wouldn't want to discuss a design pattern with a guy like you ;) "

"Ok lets come to the point. We not going to discuss on what the State pattern is here, Instead we will discuss about how to make it better from its conventional model.. Deal? Lets look at the image"

Source : wikipedia

"This is a very general model But lets lists down the common issues with this "

"The Context should not be exposed to external entities. We should have an interface for it and the external elements should only use that interface"

"Why ?"

"Because Context has the internal details of the state and the control of the state transitions. So it is pretty dangerous. The complete state model must be hidden behind an interface and it should not have transparency"

"Do you mean, we should not even expose the current state to out side?"

"Yes of course. Ideally this context should do nothing but delegates the interface's calls to the current state. Even the state transitions must be taken care by those respective states. Context should not do it "

"Jesus, I am bit confused. Can you draw and explain ? "

"Yes, lets draw, open your Jude please ! We will discuss on the different state transitions of a patient admitted to hospital. The domain is very simple, but will be comfortable to discuss"


"So lets discuss on this now. We will just list the major differences from the conventional state pattern.

1) Having Patient interface and PatientFactory abstract class. Well conventionally we will let users to use Context classes as the state. But now the idea is, we should not even expose the state to others. Instead we just provide an interface of your domain and hide the implementation and the concept of the State within it. So I keep a Patient interface and having a Factory class to create it. I don't even want expose the implementation class to others. Thats the reason why I came up with this factory."

"Wait JK, this seems ok. But it has nothing to do with State pattern rite ? "

"Yeah ma'm, it is not the state pattern. But The biggest mistake we make always is learning only the pattern. But miss out the practice. It took me years to notice Null Object Pattern which is always to be associated with State pattern, even though I knew State pattern long while ago. You not going to make use of these patterns if you don't know the best practices of them Thats why we have few more patterns mingled with State pattern to get the full use of it .. clear ?"

"Yes thats fine ...(Whispering .. Better this guy talk less and do more, sometimes very irritating !) So you have a factory to create patient, and PatientContext is the realization of it ?"

"Spot on, PatientContext is the normal context class we use in the conventional pattern too. But only difference here is,  the state transition is not controlled by PatientContext here"

"Oh that's confusing. normally what I do is , I change the state in the each life cycle method from context. Here you don't do it"

"Yes correct, Instead I just delegate it to each State.Because State is the best person to know whom to be moved next right ?"

"Well it looks fine, but what the hell is this StateTransition interface. It has a changeStateTo method."

"Ha ha, thats the wow factor of this model :) .. You have to change the state of the context end of the day. This is done by all the State classes. So State classes should be given the reference of the Context. So I have three of options. One is to have this method in the Patient interface"

"Buts that will crap your whole design, you will then expose the transition method to outside"

"So nice of you dear, we have a good wavelength ! , so the next option is to have it in PatientContext class itself"

"Thats a better idea, we can get rid of this StateTransition interface"

"But then again, its against the nature of the design. Here we make everything using interface. So the individual States don't need to be aware of the StateContext class. Hence you can give the developments of State classes to different "outsourced" people without having your core Context class affected. Safe rite ?"

"Jesusssssssss ... Do we have to think that deep? "

"At least on paper baby ...Probably its my paralysis by analysis outcome :) "

"But anyway it sounds really good, This could seriously go into Bridget Jone's Diary"

"Ha ha I wish ... So shall I write the codes for you"

"Yes please JK"

"Ok lets start with the PatientFactory.java"

package jk.cool.statepattern.java1_4;

/\*\*
\* PatientFactory to create empty Patient object.
\* The reason to have the Factory to prevent people to create PatientContext instace adhocly
\*/
public abstract class PatientFactory {
    public static Patient createPatient(){
        return new PatientContext();
    }
}

Notable points :

1) Its an abstract class.

2) Don't confuse with Builder, Factory and AbstractFactory patterns. Although it gets closer to Factory pattern, it is not!

Ok now the next item is Patient.java ... very simple

package jk.cool.statepattern.java1_4;


/\*\*
\* The Patient interface which keeps the lifecycle method of patient admitted in a hospital
\*
\* \*/
public interface Patient {

    public void admit();

    public void changeWard();

    public void discharge();

    public void pronounceDead();
}

Notable points :

1) All the life cycle methods are here.

2) Renée Zellweger won Oscar for Best Supporting Actress for the movie Cold Mountain (Year 2003)

Now lets get into the core class. PatientContext.java

package jk.cool.statepattern.java1_4;

/\*\*
 \* Realisation of a patient inteface which delegates the method calls to current state object of the Patient
 \* \*/
class PatientContext implements Patient, StateTransition{
    private PatientState patientState;

    /\*\*
     \* Restrict the access as we not going to allow others to instantiate this class
     \* \*/
    PatientContext() {
        changeStateTo(StateTransition.NEW_PATIENT);
    }

    public void admit() {
        patientState.admit(this);
    }

    public void changeWard() {
        patientState.changeWard(this);
    }

    public void discharge() {
        patientState.discharge(this);
    }

    public void pronounceDead() {
        patientState.pronounceDead(this);
    }

    public void changeStateTo(PatientState patientState) {
     this.patientState = patientState;
    }

}



Notable Points :

1) Not having a public constructor. Means outsiders don't have the control to create.

2) lifecycle methods are delegated to PatientState. PatientContext doesn't aware of any of the state transition :)

3) changeStateTo() is from StateTranstion interface. This method can only be called by State classes. Patient interface doesn't aware of it.

Now just a brief on StateTransition.java interface

package jk.cool.statepattern.java1_4;

import jk.cool.statepattern.java1_4.states.\*;

/\*\*
 \* States are value object. Hence creates the set in this interface.
 \* \*/

public interface StateTransition {
    public static final PatientState NEW_PATIENT = new NewPatientState();
    public static final PatientState ADMITTED_PATIENT = new AdmittedPatientState();
    public static final PatientState DISCHARGED_PATIENT = new DischargedPatientState();
    public static final PatientState DEAD_PATIENT = new DeadPatientState();
    public static final PatientState NULL_PATIENT = new NullPatientState();
    void changeStateTo(PatientState patientState);
}

I just defined all the possible States in this interface as States are value objects. Lets move in to PatientState.java

package jk.cool.statepattern.java1_4;

/\*\*
\* PatientState interface which will have the realisations of the lifecylce methods
\* \*/

public interface PatientState {
    public void admit(StateTransition stateTransition) ;

    public void changeWard(StateTransition stateTransition);

    public void discharge(StateTransition stateTransition);

    public void pronounceDead(StateTransition stateTransition);

}


Near identical to Patient methods. But having the StateTransition reference to change the next state. I better introduce a real State class to get more understanding now! A patient may have Admitted, Discharged, Dead states. So I take Admitted state now

package jk.cool.statepattern.java1_4.states;

import jk.cool.statepattern.java1_4.StateTransition;

/\*\*
\* From this state , patient can be moved to various other states .. ofcourse

\*/
public class AdmittedPatientState extends NullPatientState {

    @Override
    public void changeWard(StateTransition stateTransition) {
        System.out.println("The patient ward is changed !!!, Nothing special");
        stateTransition.changeStateTo(StateTransition.ADMITTED_PATIENT);
    }

    @Override
    public void discharge(StateTransition stateTransition) {
        System.out.println("The patient is discharged !!!, Nothing special");
        stateTransition.changeStateTo(StateTransition.DISCHARGED_PATIENT);
    }

    @Override
    public void pronounceDead(StateTransition stateTransition) {
        System.out.println("The patient is pronounced dead as he deserved !!!");
        stateTransition.changeStateTo(StateTransition.DEAD_PATIENT);
    }
}

"Look how these life cycle methods have been implemented. The business logics as well as the state transition to next state are covered here. Get my point? "

"Hey JK, you made a silly mistake. Every state should implement PatientState transition. You forgot to do it. Also where the hell this admit method ? You got to implement it rite ?"

"Wow you are following it then ... This is the time we desperately need Null Design Pattern. Answer my question now ... I need fact"

"Yes"

"Why the hell we need admit method here.. after all admitted patient cannot be admitted again .. No real sense ?"

"Yes true .. but the method is there to implemented rite ?"

"That's why we need to introduce Null pattern. Look at the NullPatientState.java class"

package jk.cool.statepattern.java1_4.states;

import jk.cool.statepattern.java1_4.PatientState;
import jk.cool.statepattern.java1_4.StateTransition;

/\*\*
\* Null state to represent the nostate behaviour of a patient.
\* Formerly in state patterns this has been an abstract class.
\* But by introducing a Null pattern, this will become more reusable
\*/
public class NullPatientState implements PatientState {

    public void admit(StateTransition stateTransition) {
        throw new UnsupportedOperationException("This operation is not supported at this             state");
    }

    public void changeWard(StateTransition stateTransition) {
        throw new UnsupportedOperationException("This operation is not supported at this             state");
    }

    public void discharge(StateTransition stateTransition) {
        throw new UnsupportedOperationException("This operation is not supported at this             state");
    }

    public void pronounceDead(StateTransition stateTransition) {
        throw new UnsupportedOperationException("This operation is not supported at this             state");
    }
}

"See these methods do nothing but throw exceptions "

"I get it, by extending these class, you can opt out of any unwanted life cycle methods to be used am I rite ? So nice of you "

"Yep pretty cool.. You can have this code with you. I save it here"

"Wait wait, why don't we just write a main class and test it ?"

"Why not?"

package jk.cool.statepattern.java1_4;

/\*\*
\* Main test class
\*/
public class Test {
    public static void main(String[] args) {
        Patient tomCruise = PatientFactory.createPatient();//Creating the patient
        tomCruise.admit(); //admitting the patient
        tomCruise.changeWard(); //Changing the ward
        try {
            tomCruise.admit(); //trying to admit an admitted patient. Error expected
        } catch (Throwable e) {
            //This operation is not supported hence getting an error
            System.out.println(e.getLocalizedMessage());
        }
        tomCruise.discharge(); //discharging the patient
        try {
            tomCruise.changeWard(); //Trying to change the location for patient who is not             admitted
        } catch (Throwable e) {
            //This operation is not supported hence getting an error
            System.out.println(e.getLocalizedMessage());
        }
        tomCruise.admit(); //trying to readmit
        tomCruise.pronounceDead(); //pronouncing the patient dead
        try {
            tomCruise.admit(); //trying to admit dead patient. Error expected
        } catch (Throwable e) {
            //This operation is not supported hence getting an error 

            System.out.println(e.getLocalizedMessage());
        } 

    }

}

"So its pretty good rite ? "

Source : http://img.dailymail.co.uk

"Yeah sounds good. I will better use it in my domain model. Thanks a lot JK, nothing can beat this State pattern I can promise you"

While we had been discussing this, there is a guy(?) who occupied the windows seat, all the time following our talk. Now he jumps into the discussion.

"Hi folks, I am Tom Cruise (!). I am just following what you two have been discussion. May I have my say ?"

"What ? You too belong to IT industry" , Me irritated and asked

"Of course, if you too are, then why not me? "

"Ok what you going to say ?"

"This state patterns seems ok. But there are too many classes and codes. I would rather use Java enums for implementing this"

"What? Are you serious. How you can use it", I become little annoyed now

"Oh really Pom, Shall we have a try then" Renee now jumps in and asks....

Well if you want to have the State Pattern implemented in java enums ... Stay Tuned for me next post .. We are not yet landed yet !!!!

The Source for this samples can be found here in this link:

Good References :

http://en.wikipedia.org/wiki/State_pattern

http://joesbitbucket.blogspot.com/2007/06/state-pattern-persistence-with.html

http://java-x.blogspot.com/2006/12/implementing-state-pattern-in-java.html


Comments:

Hey JK.

Nice article, I was currently looking for info about the State pattern since I need to implement it today, and I just read a little about it a few months ago.
Coupling it with the Null Pattern is so logicial, though I wasn't going to do it that way.

Since I'm working on it starting today, it's too bad your next article isn't out yet (well this one is perfect for my use case so it's ok, but it's nice and funny to read your post ;)).

Hoo and by the way, it's too bad smileys don't work, you need to fix that :p.

Gama

Posted by Gamabounta on January 09, 2009 at 01:20 PM SGT #

I forgot, there is a little mistake in your UML diagram. The type of the patientState variable is not Patient but PatientState.

Great work again!

Posted by Gamabounta on January 09, 2009 at 02:42 PM SGT #

Hi Gamabounta,
Thanks for your comments. Corrected the mistakes.
Regards,
JK

Posted by JK on January 18, 2009 at 02:34 AM SGT #

You're welcome.
I'm looking forward to read your next article on the State Pattern. Your style is funny and the content valuable.

Regards,
Gama

Posted by Gamabounta on January 19, 2009 at 01:51 PM SGT #

Oh my god, why have I found this page so late?! Having been googling for days with too much disappointment, I even read and read the book Patterns in Java I bought years ago and failed to grasp state pattern. I really want to give JK a big hug when I followed down the the bottom of this page here! For those who are curios, God guided me to use this phrase in google's input box this morning: "java when to use state pattern when not". And "State Design Pattern using java .. A different approach !" appeared the 2nd in result page!

Posted by Zhijun on March 03, 2009 at 12:44 PM SGT #

I really hope I can have a fly with JK like that lucky girl (or JK is the luckier ;-) So I don't have to post my question here:

Someone(author of a book) told me the following causing me confused:
The state objects do not exist to hold state information. They are intended to be immutable objects.

But without the domain data in concrete state objects, how can they implement the business logic for me? Where is the best place to keep domain data and how to pass it from one state to the other?

Thanks a lot JK!

Posted by Zhijun on March 03, 2009 at 12:59 PM SGT #

class PatientContext implements Patient, StateTransition

if a client class casts the instance to PatientContext after getting a Patient interface definition from the factory & invokes the changeStateTo() directly then how is this to be prevented?

PatientContext patient = (PatientContext)PatientFactory.createPatient();
patient.changeStateTo(patientState);

Posted by JavaLogix on April 22, 2009 at 11:19 AM SGT #

Gud work JK, thanks ....keen to know the ans for JavaLogix's Qs.

Posted by Mohd Amjed on April 23, 2009 at 05:54 PM SGT #

I like your article.
There seems to be a lot of confusion on the state pattern. Much of it because the GOF guys did such a poor job of describing it.

I'm not sure though, that I agree with all of what you say, but you pointed out some key items that needed to be brought to attention:
\* Creators of context objects do not create the states, that's hidden.
\* Callers of context lifecycle methods do not explicitly change the state, that's hidden.
\* Lifecycle methods in the context delegate to methods of the current state.
\* Lifecycle methods of the states act on the context object (as if the state's
method were a method of the context).
\* Callers of the context lifecycle methods generally don't know what state the context is in (and generally shouldn't care).

Posted by Python Guy on June 04, 2009 at 09:23 PM SGT #

dfgagg

Posted by guest on June 27, 2009 at 07:25 AM SGT #

Answer to Zhijun,

Someone(author of a book) told me the following causing me confused:
The state objects do not exist to hold state information. They are intended to be immutable objects.
<JK> Chicken Egg discussion :), The state objects are immutable ofcource. But I can't understand why don't they hold state information. Your statement doesn't standalone. May be with a proper justification it could be!

But without the domain data in concrete state objects, how can they implement the business logic for me? Where is the best place to keep domain data and how to pass it from one state to the other?

<JK> You can hold domain data in the concrete state object. What my point is, never expose them outside. We always need immutable domain state objects. And I don't want to confuse domain driven design with here. because that's one layer up I suppose.

Posted by JK on June 29, 2009 at 08:26 AM SGT #

Answer to JavaLogix
Good question. Look at the access control of class PatientContext. Its not public which means client code will not have access to it.

PatientContext patient = (PatientContext)PatientFactory.createPatient();

The above code is not possible in a different package.

Apologize for the late reply. I believe you might have already figured it out !

Posted by JK on June 29, 2009 at 08:37 AM SGT #

Hi Python Guy,

You basically summarized it all !

The bottom line is, we can't just blindly adapt what GOF suggests. Any design pattern will be associated with many others when implementing them. I just tried to address a practical use case.

Posted by JK on June 29, 2009 at 11:56 AM SGT #

Folks,

Sorry for the late replies. I had been idle for sometime due to few research works. Hope I will get more time blogging now and will continue this article with Enum.

Meantime I have moved back to blogger as I recently quit Sun. Hope you give me the same support!

http://jk-blogging.blogspot.com/

Posted by JK on June 29, 2009 at 11:59 AM SGT #

best

Posted by kiruthigan on October 15, 2009 at 11:04 AM SGT #

this is cool, this is what we want dude......

Posted by links of london on November 29, 2009 at 02:02 AM SGT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Jeyakumaran Chandrasegaram alias JK shares his experience as Sun Campus Ambassador for Sun here.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today