I have an interface IResponse
that represents a response to a Question
and a few concrete response types that implement IResponse
:
interface IResponse<out TQuestion> where TQuestion: Question
{
TQuestion Question { get; }
Candidate Candidate { get; }
string Answer { get; }
}
class MultipleChoiceResponse : IResponse<MultipleChoiceQuestion>
{
public MultipleChoiceQuestion Question { get; }
public Candidate Candidate { get; }
public string Answer { get; }
}
class TextResponse : IResponse<TextQuestion>
{
public TextQuestion Question { get; }
public Candidate Candidate { get; }
public string Answer { get; }
}
...
abstract class Question {..}
class MultipleChoiceQuestion : Question {..}
class TextQuestion : Question {..}
...
For each response type, I have a corresponding response evaluator that implements IResponseEvaluator
:
interface IResponseEvaluator<TQuestion, TResponse>
where TQuestion : Question
where TResponse : IResponse<TQuestion>
{
decimal Evaluate(TResponse response);
}
class DefaultMultipleChoiceResponseEvaluator : IResponseEvaluator<MultipleChoiceQuestion, MultipleChoiceResponse>
{
public decimal Evaluate(MultipleChoiceResponse response)
{
...
}
}
class DefaultTextResponseEvaluator : IResponseEvaluator<TextQuestion, TextResponse>
{
public decimal Evaluate(TextResponse response)
{
...
}
}
...
Finally, I have an EvaluationAggregator
with a method EvaluateAll
that provides a common interface to accept a collection of all subtypes of IResponse
using IEnumerable<IResponse<Question>>
:
class EvaluationAggregator
{
public IResponseEvaluator<MultipleChoiceQuestion, MultipleChoiceResponse> ChoiceEvaluator { get; set; }
public IResponseEvaluator<TextQuestion, TextResponse> TextEvaluator { get; set; }
...
public decimal EvaluateAll(IEnumerable<IResponse<Question>> responses)
{
decimal totalScore = 0;
foreach (var response in responses)
{
decimal score = response switch
{
MultipleChoiceResponse mcr => ChoiceEvaluator.Evaluate(mcr),
TextResponse tr => TextEvaluator.Evaluate(tr),
...
};
totalScore += score;
}
return totalScore;
}
}
Since, TQuestion
in IResponse<TQuestion>
is covariant(marked with out
), I can pass responses to all question subtypes as a single IEnumerable
to EvaluateAll
. However, to select the right evaluator for the type , I'm still having to switch on the type of the response. If I add more response types in the future, the code will have to be modified. Is there a design pattern or a cleaner way to do this?
This is how I would call it:
IEnumerable<IResponse<Question>> responses = FetchAllTypesOfResponses();
var aggregator = new EvaluationAggregator
{
ChoiceEvaluator = new DefaultMultipleChoiceResponseEvaluator(),
TextEvaluator = new DefaultMultipleChoiceResponseEvaluator()
};
decimal totalScore = aggregator.EvaluateAll(responses);
Aucun commentaire:
Enregistrer un commentaire