State Pattern and BusinessRules

State Pattern and BusinessRules

Old forum URL: forums.lhotka.net/forums/t/5005.aspx


czuvich posted on Sunday, June 22, 2008

Hello all,
I am having some difficulty figuring out how to apply the State Pattern to my CSLA business object. Currently I  have three different use cases for my object: Open, Pending, Distributed.

This object will need to transition between these three states in that exact order. With each state, the object validation changes. For instance, in an Open State, there are a minimum set of required fields, with Pending, the set grows by quite a bit and some extra rules are checked, and finally the Distributed state requires a much larger state along with some extra security.

How can I implement the state pattern with CSLA? Since rules are based on properties, and my object's validations are changing based on an encapsulated object, how will this work? Thanks for your help.

JonStonecash replied on Sunday, June 22, 2008

I am going to offer this suggestion without knowing any of the context or requirements.  What you have here are three different things with three different sets of behavior.  I would at least start out with three different classes, each derived from a common parent class with the properties and command behaviors.  Each of the derived children classes would have the specific rules for its state.  If you have specific methods to handle the transitions, you can make the parent class abstract (VB MustInherit), add the methods as abstract (VB MustOverride) to the parent class, and then override them in the various child classes.  I think that this will be much cleaner, much easier to code and much easier to test/debug.

The only disadvantage of this approach that comes to mind is that you cannot change an object in one state to another state, at least directly.  My initial response would be to create the instance representing the new state, delete the instance representing the old state, and insert the new instance in its place.  (I would place the methods to create the next state in the class representing the current state; the Open class would be capable of making a "copy" of itself as a Pending object, and the Pending class would be able to output a Distributed copy.  This would ensure that the order of march was strictly followed.)

I can foresee that there might be problems in doing this.  It might be hard to track down all of the references to the old object when the new object comes into existence.  If this is the case, you might want to look at a container class that holds exactly one of the children classes.  This would require writing a lot of pass thru code(from the container methods to the contained child methods) but would be very flexible.  The container would have a continuing existence, even while the contained object changed identity and type.

Just some thoughts before I go have my lunch.

Jon Stonecash

czuvich replied on Sunday, June 22, 2008

That design is exactly what I started doing initially; however, I couldn't come up with a clean way of changing state of the object. When should the line be drawn for a state pattern and a factory pattern (as you have mentioned here)?   Here are my requirements and perhaps you (or anyone) can offer a better solution than implementing a State pattern for my object:

The client creates a new object as an OPEN object. Only a small set of data is required in order to Save this object. Once the client is ready for the next stage, they will be forwarding this object for approval. During the transition, requirements change on the object. The object is then validated as a PENDING object. Once the approver thinks everything is a go, they approve it into a DISTRIBUTED state where other rules apply. Security remains the same on the object no matter what state it's in. Anyone can still edit/create; however, only certain people can DISTRIBUTE and delete.

All of this data is the same set of tables so I do want to keep the DALC in one class. Does this sound more like a State pattern with three different states or three completely different objects for each use case? If an object starts in one state, and then moves to other states, I tend to lean towards the State pattern. If an object can be created in any number of states and that state does not change, I think Factory pattern. What do you and others think?

JonStonecash replied on Monday, June 23, 2008

I f you build three classes, each of which inherit from a common parent class, the DAL logic can be coded in the parent, and thus would be common to all three classes.  I am voting for three different classes. 

Jon Stonecash

czuvich replied on Monday, June 23, 2008

Excellent. I'll give that a shot. Would you mind elaborating on constructing the container class? Maybe some pseudocode? I assume you would have methods like CreateOpen(), and then declare specific methods (methods that alter the type - Open->Pending) that pass thru to the child class? It sounds like I would need to do specific casting inside of the container class as well? Thanks.

JonStonecash replied on Monday, June 23, 2008

I do not have any pseudo code but there are two ways to do it. 

One is to build a container that totally mimics the abstract class.  That is, for each method on the abstract class there is a corresponding method on the container class.  Each method on the container class delegates to the corresponding method on the current occupant of the contained Document class.  Lots of coding for not so much value.  That is the approach that you outlined above.  I would try to avoid this approach.

The other way is to build a minimal container that basically has a read only CurrentDocument property that returns the current occupant of the contained Document Class typed as the abstract class.  Any reference to a property of the contained Document class would have to be coded as follows:

myContainer.CurrentDocument.Title = "New Title"

You would want to make sure that there were no code sequences that did something like this:

Dim myDocument as AbstractDocument = myContainer.CurrentDocument
myDocument.Title = "another new title"

You do not want naked references (e.g., myDocument) to the current contained document floating around, because the following methods would invalidate those references.

The container would also implement some methods to change the container: ChangeToPending, ChangeToDistributed and so on.  They methods would create a new contained document object that reflected the desired change.  This would become the new occupant of the Current Document property. 

You will also have to design the abstract class so that there are appropriate responces to each method call.  If the PendingClass has a SubmitForFinalApproval action, the Open and Distrtbuted classes have to implement this method, but with responses appropriate to their nature.  The Open class might throw an exception and the Distributed class might ignore the call.  This is called detailed design and I leave that to you.

Note that the abstract class can have all of the logic that is common to all three classes.  This would include data access and common validation logic.

Jon

 


czuvich replied on Monday, June 23, 2008

This sounds great! It makes perfect sense to not use the State pattern since the "magic" will be in state classes; therefore making it a bit more difficult to maintain (2 objects in one with dynamic states vs 3 defined objects for each use case sharing the same base class).  The container class will keep references clean. I really appreciate the help and advice!

Copyright (c) Marimer LLC