ASP.NET 2.0 Wizard and the State Design Pattern
Some time ago, someone asked me to create ASP.NET 2.0 application to send mails to a list of customers.
I will give you a short explanation of the problem at hand.
The list of customer numbers could be retrieved by following a couple of steps.
First we have a radiobuttonlist to make an initial choice
1. Retrieve the list of customers who where brought on by other customers.
2. Retrieve the list of customers who where brought on by employees.
3. Retrieve the list of customers who where brought on by a marketing campaign.
4. Select individual customer numbers from a listbox.
5. Input certain customer numbers.
After step 1, 2 or 3, the retrieved list has to be shown in a grid with row checkboxes, so individual (or all) customer numbers could be selected.
After this step, there has to be a possibility to refine the search on location.
So after step 1, 2 and 3 we have to go to a step to show all numbers. After this step we have to go to a refinement step.
After step 4 and 5 this refining was not necessary.
In the end all steps resulted in another step where we can select a template mail or compose one on our own.
After selecting a template or compose a message on our own, we actually send the letter.
As you can see, it's quit a conditional flow of steps.
After I read the scenario, I was thinking of using the Wizard control and as usual I was starting to code immediately without thoroughly thinking the problem through. I think I have to do something about that but the enthusiastic me takes over every time :)
In the next button click of the wizard I came up with the following code:
WizardSteps activeWizardStep = (WizardSteps) wizard.ActiveStepIndex;
if (activeWizardStep != WizardSteps.makeYourChoice)
{
switch (activeWizardStep)
{
case WizardSteps.customerThroughCustomer:
case WizardSteps.customerThroughEmployee:
case WizardSteps.customerThroughFamily:
{
// Do some step processing
wizard.ActiveStepIndex = (int)WizardSteps.showCustomerNumberList;
break;
}
case WizardSteps.showCustomerNumberList:
{
//
Do some step processing
wizard.ActiveStepIndex = (int)WizardSteps.refineCustomerList;
break;
}
case WizardSteps.choiceOfCustomerNumber:
case WizardSteps.inputCustomerNumber:
case WizardSteps.refineCustomerList:
{
// Do some step processing
wizard.ActiveStepIndex = (int)WizardSteps.choiceOfMarketingLetter;
break;
}
case WizardSteps.choiceOfMarketingLetter:
{
// Do some step processing
wizard.ActiveStepIndex = (int)WizardSteps.sendLetter;
break;
}
}
}
else
{
switch (stepsChoice.SelectedIndex)
{
case 0:
{
wizard.ActiveStepIndex = (int)WizardSteps.customerThroughCustomer;
break;
}
case 1:
{
wizard.ActiveStepIndex = (int)WizardSteps.customerThroughEmployee;
break;
}
case 2:
{
wizard.ActiveStepIndex = (int)WizardSteps.customerThroughFamily;
break;
}
case 3:
{
wizard.ActiveStepIndex = (int)WizardSteps.choiceOfCustomerNumber;
break;
}
case 4:
{
wizard.ActiveStepIndex = (int)WizardSteps.inputCustomerNumber;
break;
}
}
}
I used following enum for the wizard steps
public enum WizardSteps
{
UnknownStep = -1,
MakeYourChoiceStep = 0,
CustomersThroughCustomerStep = 1,
CustomersThroughEmployeeStep = 2,
CustomersThroughCampaignStep = 3,
ChoiceOfCustomerNumbersStep = 4,
InputCustomerNumbersStep = 5,
ShowCustomersNumberListStep = 6,
RefineCustomerListStep = 7,
ChoiceOfMarketingLetterStep = 8,
SendLetterStep = 9
}
From the moment I started writing it, it felt like smelly code. Maintenance would be horrible. Adding or moving some of the steps would become tricky. No to mention all the design principles who were being violated. Like SRP, Open Closed, and probably some more which I didn't noticed.
I got home and took my book "Head First Design Patterns". The book is actually written for the Java community but it read so appealingly, that I finished it in a couple of days. After reading some of the chapters, it hit me that maybe I could use the "State Design Pattern". The whole scenario sounded like a state machine. I took the basic principle of the pattern and altered a few things so it looked like the following:
Click the picture if you want a sharper image.
So what do we have here:
- WizardFlowControl: acts as the Context Object
This object delegates all the work to the correct step. The steps determine who their next step will be. The WizardFlowControl will be instantiated when the next button gets clicked. You can think of this class as the hearth. It will regulate everything.
- Step: acts as the state interface
Step is an abstract class. It receives the actual wizard and the viewstate as a statebag. The class also has an abstract readonly property StepIndex which has to be implemented by the class inheriting Step. It also contains an abstract method Handle which has to be implemented. You can access the wizard and viewstate trough some base properties.
- Implementation classes: Inherits Step
Let's see how we can code it. We will begin with the base class Step. It look like this:
public abstract class Step
{
private Wizard _wizard;
private Step _nextStep;
private StateBag _viewState;public abstract int StepIndex {get;}
public abstract void Handle();public Wizard Wizard
{
get{return _wizard;}
set {_wizard = value;}
}
public Step NextStep
{
get {return _nextStep;}
set {_nextStep = value;}
}
public StateBag ViewState
{
get {return _viewState;}
set {_nextStep = _viewState;}
}
}
As you can see, no rocket science here. Just an abstract class with four properties, one of them abstract, and an abstract method.
When we take a look at one of the implementation classes, ex. CustomersThroughCustomer, it looks like this:
public class CustomersThroughCustomer : Step
{
public CustomersThroughCustomer(){}public override int StepIndex
{get { return (int)WizardSteps.CustomersThroughCustomerStep; }
}public override void Handle()
{// Process The Step
// Transition to the new state
NextStep = new ShowCustomersNumberList();}
}
A little explanation might come in handy here. The readonly StepIndex property returns us the current integer stepindex of the wizard. In the Handle method we do the actual processing. For example we can call some kind of service to get me all customers. Then this list can be bound to a listbox or some other control. After the processing, we set the nextstep. Here we are transitioning to another state.
But something doesn't feel right about the transitioning code. We can clearly see some tight coupling issues here. The next step instantiation just feels awkward. Maybe we can use another design pattern here.
I decided to use the "Factory Design Pattern". I implemented it, using reflection, like this:
public static class StepFactory
{
public static Step CreateStepInstance(string stepTypeName, Wizard wizard, StateBag viewState)
{
Assembly assembly = Assembly.LoadFrom("WizardLib.dll");
string assemblyName = assembly.FullName;ObjectHandle wrappedStep = Activator.CreateInstance(assemblyName, stepTypeName);
Step step = (Step)wrappedStep.Unwrap();
step.Wizard = wizard;
step.ViewState = viewState;
return step;
}
}
Note: for more information you can read my other article Creating instances using reflection
Now that we have our factory, let's use it and change the following code:
// Transition to the new state
NextStep = new ShowCustomersNumberList();
By this one:
// Transition to the new state
string nextStepName = Wizard.WizardSteps[(int)WizardSteps.ShowCustomersNumberListStep].ID;
NextStep = StepFactory.CreateStepInstance(nextStepName, Wizard);
First we retrieve the name of the ID of the next wizardstep by getting it out of the WizardStep collection using the correct enumeration WizardStep item.
Then we ask the factory to give us the correct next step instance.
Now let's take look at the heart of the wizard, the WizardFlowControl:
public class WizardFlowControl
{
Wizard _wizard;
Step _activeStep;
StateBag _viewState;public WizardFlowControl(Wizard wizard, StateBag viewState)
{
_wizard = wizard;
_viewState = viewState;
}public void Handle()
{
ProcessStep();
SetNextStep();
}
private void ProcessStep()
{
// Get Current Step
string activeStepName = _wizard.ActiveStep.ID;
_activeStep = StepFactory.CreateStepInstance (activeStepName, _wizard, _viewState);// Process the active step
_activeStep.Handle();
}
private void SetNextStep()
{
_wizard.ActiveStepIndex = _activeStep.NextStep.StepIndex;
}
}
What's going on here: The WizardFlowControl accepts the wizard and the viewstate.
For loose coupling I comply to the Inversion Of Control (IoC) principle by implementing the Dependency Injection pattern. This is well described by the master itself, Martin Fowler, in the following article http://www.martinfowler.com/articles/injection.html.
After instantiation (in the next button click) we then call Handle. Handle will first determine the active stepindex and asks the factory for the active Step instance. It then calls the Step's Handle method and the step gets processed.
Finally the wizard is set to the next step, which is determined by the step itself.
That's actually it.
Conclusion:
The huge switch-case statement in the next button click of the page is now replaced by two statements:
- WizardFlowControl wizardFlowControl = new WizardFlowControl(this.wizard, this.ViewState);
- wizardFlowControl .Handle();
All responsibility is now handed over to the separate step implementations.
We someone asks me to add a step. It is easily done by creating a new class which inherits from the abstract base class Step. We can easily configure its position in the flow by setting it's stepindex to the correct step. And finally we have to decide which step is its predecessor and its successor.
Finally I would point out some of the issues that still bother me.
The fact that we have to name the step implementation classes the same as the wizard step Id's.
We can easily change this and use a config file to create key/value pairs for the wizardstep/step implementation combinations.
The second issue is related to the fact that I implemented the state design pattern in an ASP.NET web app. What about the ViewState?
You cannot access the ViewState from inside the step implementation classes. So I decided to pass the entire viewstate into the the step instances by providing it to the factory. This is probably not a good idea, but I didn't see an other possibility. I really needed the viewstate in the different steps.
If someone reads this post and has some remarks about it, please feel free to comment. I'm still in my learning phase, so I can use every help I can get.
Greetz,
G
Labels: ASP.NET 2.0, State Design Pattern, Wizard
10 Comments:
You should take a look at Windows Workflow Foundation and if using WF is not an option you should switch jobs. Anyway you have implemented a sequential workflow where your steps are defined in a DSL and I am glad to find out that you have chosen to not go for the fugly switch block :-)
By Anonymous, At 15 March 2007 at 15:38
Then the article about the solution using Windows Workflow Foundation will be following soon. Got any book propositions for me? :)
By GSharp, At 15 March 2007 at 18:00
Essential WF: is a bit theoretical but shows you how WF was created and why including samples.
WF Step by Step: very practical book including lots of screencaps that show you how to use WF.
By Anonymous, At 15 March 2007 at 20:03
Not passing the entire viewstate was an issue.
I solved it in my article of april
"Serializing viewstate to object"
Greetz
By GSharp, At 18 April 2007 at 07:20
Keep up the good work.
By Anonymous, At 11 November 2008 at 08:56
I think from a purely design perspective, this pattern makes sense and is an elegant way of designing a wizard. From a pragmatic point of view however, I don't think this approach offers anything more than the "fugly" switch block. From my point of view it actually takes longer to build and makes maintenance more difficult. Wizards are UI based things so it just adds to the complexity of managing the UI state/transitions (I've only built WinForms based wizards so not sure if this applies to Asp.NET). Also the maintenance in adding a new step is distributed around - in a switch block you just add a new case and all the code is there in the one procedure whereas with the class based abstraction you have to add the class for the new step and then hunt around the other step classes to fix the transitions. I've built wizards using both the "big switch" pattern(!) and the state pattern and found actually that, unless you're building some kind of extensible, wizard framework, the old school switch statement way works fine and is actually quicker and easier.
By John Rhodes, At 18 November 2009 at 10:56
Hi. May be I don't understand something. How are you going to bind anything inside steps' Handle method? Are you going to access the controls on the page thru _wizard object?
By Unknown, At 17 August 2010 at 04:28
fzqc fleol [URL=http://www.bigtits234.com]Juggs[/URL] vtwmqx t pq r sru
By Anonymous, At 16 January 2011 at 18:28
xleq ncruu [URL=http://www.katesxxx.com]video sex[/URL] lvypbn i ey f kth
By Anonymous, At 6 February 2011 at 18:11
Very nice post!
Let's say while processing a step you need to, not only define the next step, but actually start executing it.
Would it be wise to just do
NextStep.Handle();
from within the current step ?
Thanks in advance!
Cheers,
Ricardo
By Ricardo, At 8 March 2012 at 15:16
Post a Comment
Subscribe to Post Comments [Atom]
<< Home