I have written some survey functionality for a project. Basically, a generic survey form that can be composed of sections and questions.
I have a Survey
class, Questions
and Sections
. The Survey
is basically a tree, where each node can be a Question
or a Section
. Nodes have children -- so essentially Question
can have a collection of subsections and subquestions, and a Section
can have a collection of subsections and subquestions.
The nodes in my Survey
have derive from the abstract class SurveyPart
.
namespace Surveys
{
public abstract class SurveyPart
{
public abstract List<SurveyPart> Children { get; set; }
}
public class Survey
{
public List<SurveyPart> Children { get; set; }
public Survey()
{
Children = new List<SurveyPart>();
}
}
public class Question : SurveyPart
{
public override List<SurveyPart> Children { get; set; }
public string QuestionText { get; set; }
public Question()
{
Children = new List<SurveyPart>();
}
}
public class Section : SurveyPart
{
public override List<SurveyPart> Children { get; set; }
public string Header { get; set; }
public Section()
{
Children = new List<SurveyPart>();
}
}
}
As far as I understand this is the Composite pattern? Not sure I've got it entirely right.
So with that I can build a survey (at present with the sections and questions coming from a DB.) Next thing is to render it. For that I'm attempting to use the Visitor pattern implemented with extension methods.
namespace ExtensionMethods
{
using Surveys;
public static class SurveyTextRenderer
{
public static int Depth;
public static void Write(this Survey survey)
{
Depth = 0;
Console.WriteLine("Survey");
Console.WriteLine(new string('-', "Survey".Length));
foreach (SurveyPart child in survey.Children)
{
Depth++;
child.Write();
Depth--;
}
}
public static void Write(this SurveyPart part)
{
if (part is Section)
(part as Section).Write();
if (part is Question)
(part as Question).Write();
}
public static void Write(this Section section)
{
Console.Write(new String('\t', Depth));
Console.WriteLine("S:" + section.Header);
foreach (SurveyPart child in section.Children)
{
Depth++;
child.Write();
Depth--;
}
}
public static void Write(this Question question)
{
Console.Write(new String('\t', Depth));
Console.WriteLine("Q: " + question.QuestionText);
foreach (SurveyPart child in question.Children)
{
Depth++;
child.Write();
Depth--;
}
}
}
}
It all works OK -- if I set up the following mock survey:
Survey survey = new Survey
{
Children = new List<SurveyPart>
{
new Section
{
Header = "Section 1",
Children = new List<SurveyPart>
{
new Question { QuestionText = "Foo?" },
new Question { QuestionText = "Bar?" },
new Question { QuestionText = "Barry?" }
}
},
new Section
{
Header = "Section 2",
Children = new List<SurveyPart>
{
new Question
{
QuestionText = "Did you like it?",
Children = new List<SurveyPart>
{
new Section
{
Header = "If you answered yes, please answer the following",
Children = new List<SurveyPart>
{
new Question { QuestionText = "How come?" },
new Question { QuestionText = "How much did you like it?" }
}
}
}
},
new Question { QuestionText = "Please leave a comment" },
}
}
}
};
and call
survey.Write();
I get:
Survey
------
S:Section 1
Q: Foo?
Q: Bar?
Q: Barry?
S:Section 2
Q: Did you like it?
S:If you answered yes, please answer the following
Q: How come?
Q: How much did you like it?
Q: Please leave a comment
So basically to sum up, I'm trying to use the Composite pattern to allow for a tree of sections and questions. Then to navigate and render this tree I'm trying to use the Visitor pattern.
A few questions occurring to me:
- I'm using the static member
Depth
to keep track of how deep the visitor has gone. Could it be problematic having state on my extension methods static class? - Would the SurveyPart abstract class make more sense as an interface?
- Am I using Visitor and Composite in the right way, or am I borking them up?
(Note I've left a lot a parts out... e.g. different types of questions, scores/responses to questions, etc... just focused on the tree/Composite/Visitor parts of the code for now.)