patterncsharpMinor
Composite and Visitor patterns for tree-based survey functionality in C#
Viewed 0 times
surveypatternsvisitorforcompositebasedandfunctionalitytree
Problem
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
The nodes in my
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)
{
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 Children { get; set; }
}
public class Survey
{
public List Children { get; set; }
public Survey()
{
Children = new List();
}
}
public class Question : SurveyPart
{
public override List Children { get; set; }
public string QuestionText { get; set; }
public Question()
{
Children = new List();
}
}
public class Section : SurveyPart
{
public override List Children { get; set; }
public string Header { get; set; }
public Section()
{
Children = new List();
}
}
}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)
{
Solution
That design will work well for multiple renderers - when you stated in the comment that you would it made much more sense. You should remove the static
Note the error handling - what happens if you add a new SurveyPart and don't update the renderer? Also, I changed the name to use a Survey name, which you should see how to implement easily.
I did not implement
and extend that. It may or may not be useful to you. It depends on your calling code whether it would be worth adding that abstraction. If you have a method that is called like
Hopefully at least this gives you a few ideas!
Depth variable though - it makes your code not thread-safe. Also, you can reduce the number of overloads. Here's a way to refactor your renderer:public class SurveyTextRenderer
{
public Write(Survey survey)
{
Console.WriteLine(survey.Name);
Console.WriteLine(new string('-', survey.Name.Length);
for (SurveyPart part in survey.Children)
{
processNode(part, 0);
}
}
protected void ProcessNode(SurveyPart part, int depth)
{
if (part is Section)
WriteSection(part as Section, depth);
else if (part is Question)
WriteQuestion(part as Question, depth);
else
// Error handling or default case
for (SurveyPart part in survey.Children)
{
ProcessNode(part, depth + 1);
}
}
}Note the error handling - what happens if you add a new SurveyPart and don't update the renderer? Also, I changed the name to use a Survey name, which you should see how to implement easily.
I did not implement
WriteSection and WriteQuestion; they'll be pretty close to what you have already except that the recursion is removed. I don't think you really need statics in this case, but you can make them static if you want. However, you could make, say, public abstract class Renderer
{
public abstract void Write(Survey survey);
}and extend that. It may or may not be useful to you. It depends on your calling code whether it would be worth adding that abstraction. If you have a method that is called like
PrintSurvey(new TextRenderer(), datasource) where datasource is one of multiple places the survey could be stored (XML, database, file, etc.) it might be useful. You don't want to repeat yourself. In fact, you could have Survey extend SurveyPart (maybe rename it SurveyElement?) and remove the sort-of-redundant Write()->ProcessNode() calls.Hopefully at least this gives you a few ideas!
Code Snippets
public class SurveyTextRenderer
{
public Write(Survey survey)
{
Console.WriteLine(survey.Name);
Console.WriteLine(new string('-', survey.Name.Length);
for (SurveyPart part in survey.Children)
{
processNode(part, 0);
}
}
protected void ProcessNode(SurveyPart part, int depth)
{
if (part is Section)
WriteSection(part as Section, depth);
else if (part is Question)
WriteQuestion(part as Question, depth);
else
// Error handling or default case
for (SurveyPart part in survey.Children)
{
ProcessNode(part, depth + 1);
}
}
}public abstract class Renderer
{
public abstract void Write(Survey survey);
}Context
StackExchange Code Review Q#1729, answer score: 5
Revisions (0)
No revisions yet.