samedi 19 décembre 2015

Which design pattern to use and how for survey platform?

I'm trying to solve the best architecture for a survey system and which design patterns to use. We have the following type of questions:

  • One correct answer is possible - dropdown or radio buttons
  • Multiple choice - checkboxes
  • Yes/No - radio buttons with Yes/No
  • Short Text
  • Long Text
  • Number - e.g. When was ...?
  • Correct order - e.g. order the answers in the correct order

I'd like to have Drawing API and Validating API So I came up with "Bridge Pattern" which is mostly used for drawing. But there's something I'm missing.

What I tried is this:

public abstract class Question : IDrawable, IValidatable
{
    protected readonly IQuestionFormatter questionFormatter;
    protected readonly IQuestionValidator validator;

    public string Title { get; set; }

    public Question(IQuestionFormatter questionFormatter, IQuestionValidator validator)
    {
        this.questionFormatter = questionFormatter;
        this.validator = validator;
    }

    public abstract void Draw();

    public abstract bool Validate();

}

    public interface IQuestionValidator
{
    bool ValidateQuestion(IEnumerable<string> userInput, 
                          IEnumerable<string> questionAnswers);
}

    public interface IQuestionFormatter
{
    string FormatQuestion(string title, IEnumerable<string> options);
}

Then I create RefinedAbstractions for MultipleChoiceQuestion, OnePossibleAnswerQuestion and ShortAnswer. The problem is that MultipleChoiceQuestion can have many correct Answers and many UserInput.

public class MultipleChoiceQuestion : Question
{
    public List<string> Options { get; set; } = new List<string>();
    public List<string> Answers { get; set; } = new List<string>();
    public List<string> UserInput { get; set; } = new List<string>();

    public MultipleChoiceQuestion(IQuestionFormatter questionFormatter, IQuestionValidator validator) : base(questionFormatter, validator)
    {
    }

    public override void Draw()
    {
        var result = questionFormatter.FormatQuestion(Title, Options);
        Console.WriteLine(result);
    }

    public override bool Validate()
    {
        return validator.ValidateQuestion(UserInput, Answers);
    }
}

OnePossibleAnswerQuestion has many Answers but One UserInput.

 public class OnePossibleAnswerQuestion : Question
{
    public string UserInput { get; set; }
    public string Answer { get; set; }
    public List<string> Options { get; set; } = new List<string>();

    public OnePossibleAnswerQuestion(IQuestionFormatter questionFormatter, IQuestionValidator validator) : base(questionFormatter, validator)
    {
    }

    public override void Draw()
    {
        var result = questionFormatter.FormatQuestion(Title, Options);
        Console.WriteLine(result);
    }

    public override bool Validate()
    {
        return validator.ValidateQuestion(new List<string> { UserInput }, new List<string> { Answer });
    }
}

ShortAnswer has one Answer and one UserInput. I'm not sure how to create the design. public class ShortAnswer : Question { public string UserInput { get; set; } public string Answer { get; set; }

    public ShortAnswer(IQuestionFormatter questionFormatter, IQuestionValidator validator) : base(questionFormatter, validator)
    {
    }

    public override void Draw()
    {
        var result = questionFormatter.FormatQuestion(Title, new List<string> { "" });
        Console.WriteLine(result);
    }

    public override bool Validate()
    {
        return validator.ValidateQuestion(new List<string> { UserInput }, new List<string> { Answer });
    }
}

But when I got to the OnePossibleAnswerQuestion or ShortAnswer the .Validate() .Draw() started smelling. Probably I have defined wrong interfaces?

My goal is to have something like this:

    List<Question> questions = new List<Question>();

    // ... add some questions using Builder or Factory Design Pattern

    // draw the questions
    foreach (var question in questions)
    {
        question.Draw();
    }

    // validate the questions
    foreach (var question in questions)
    {
        question.Validate();
    }

Could you please help me what I miss and how to composite the classes? Am I using the wrong design pattern?

Thanks in advance!

Aucun commentaire:

Enregistrer un commentaire