I have doubts about this design:
public abstract class Answer {
private long id;
private Question question;
public Answer(Question question) {
this.question = question;
}
abstract List<String> getAnswers(){
}
}
And subclassed:
public class SingleAnswer extends Answer {
private String answer;
public SingleAnswer(Question question) {
super(question);
}
public List<String> getAnswer() {
return Collections.singletonList(answer);
}
public void setAnswers(Set<String> answers) {
if(answers.size()!=1)
throw new IllegalArgumentException("You have to provide only one answer");
this.answers = answers;
}
}
public class MultiAnswer extends Answer{
private List<String> answers;
public MultiAnswer(Question question) {
super(question);
}
public List<String> getAnswers() {
return answers;
}
public void setAnswers(Set<String> answers) {
if(answers.isEmpty())
throw new IllegalArgumentException("You have to provide at least one answer");
this.answers = answers;
}
}
MultiAnswer object can have more than one answer (like checkbox) and SingleAnswer object can have only single answer (like answer for open question, or single choice a,b,c,d question).
Now, I want get list of Answer objects and find out what answers they have and use them for some comparison. I make abstract method List getAnswers() for thast purpose but I have doubts if this is a good design, because SingleAnswer can hold only one value/answer and signature of method getAnswers indicated that SingleAnswer can return more than one answer.
Is it good solution or can it be solved in different way?
UPDATE:
I am writing some kind of mapper of questions that can be find on websites (like online questionaire,job offer websites and so on) that I want save in database. Then when user answer this questions, applications should fill out form on website and automatically send it.
So, when user answer questions, I need to map this questions back from database to form on website, and that why i wanted to use method ListgetAnswers() to populate form on website with proper values from Answer object.
Here is some code of:
public void populateFormElements(List<FormElement> elements, ProfileData data){
for(FormElement element:elements){
//find by question
Optional<Answer> answer= data.getAnswerList().stream().filter(a->a.getQuestion().equals(element.getQuestion())).findAny();
if(answer.isPresent()){
element.setValue(answer.get().getAnswers());
}
} }
Here is example of FormElement:
public class RadioElement extends FormElement {
private String identifier;
private String label;
private String value;
private final Set<String> options;
@Override
public Collection<String> getAvailableOptions() {
return Collections.unmodifiableCollection(options);
}
@Override
public void setValue(List<String> values) {
if(values.size()!=1)
throw new IllegalArgumentException("Too many answers. Only one answer required");
if(options.contains(values.get(0))
this.value=value;
else
throw new IllegalArgumentException("Argument has not been found in available options");
}
@Override
public Map<String, String> generateParameters() {
if(value==null)
throw new IllegalArgumentException("Value is not set");
return Collections.singletonMap(identifier,value);
}
@Override
public Question getQuestion() {
SelectQuestion question=new SelectQuestion();
question.setQuestion(label);
options.forEach(question::addAnswer);
return question;
}
Here is Question object. I use method Answer answer(String...answers) to create and validate Answers.
public abstract class Question {
private long id;
private String question;
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public abstract Answer answer(String...answers);
}
public class SelectQuestion extends Question {
private Set<String> possibleAnswers=new HashSet<>();
@Override
public Answer answer(String... answers) {
if(answers.length!=1)
throw new IllegalArgumentException("Only single answer!");
if(!possibleAnswers.contains(answers[0]))
throw new IllegalArgumentException("Answer not available in possible options");
SingleAnswer answer=new SingleAnswer(this);
answer.setAnswer(answers[0]);
return answer;
}
}
public class OpenQuestion extends Question {
@Override
public Answer answer(String... answers) {
if(answers.length!=1)
throw new IllegalArgumentException("Only single answer!");
SingleAnswer answer=new SingleAnswer(this);
answer.setAnswer(answers[0]);
return answer;
}
}
public class MultiSelectQuestion extends Question {
private Set<String> possibleAnswers=new HashSet<>();
@Override
public Answer answer(String... answers) {
if(answers.length>possibleAnswers.size())
throw new IllegalArgumentException("You provided too many answers");
Set<String> answersTemp=new HashSet<>();
for(String answer:answers)
if(!possibleAnswers.contains(answer))
throw new IllegalArgumentException("Not in possible answers");
else
answersTemp.add(answer);
MultiAnswer answer=new MultiAnswer(this);
answer.setAnswers(answersTemp);
return answer;
}
}
I wonder, if method getAnswers() returning list of answers with size=1 for SingleAnswer is Ok? Because from reading method signature we can think that SingleAnswer can returns more than one answer. Or maybe I just too much overthinking about this?
Defining it in the abstract base class :
abstract List<String> getAnswers();
is not good or bad in itself.
According to your need :
Now, I want get list of Answer objects List and find out what answers they have and use them for some comparison.
I think that the question you should ask yourself is how to perform the comparison between submitted answer(s) and expected answer(s) whatever the Answer subclass is.
First, your model doesn't make the distinction between submitted and expected answers.
So you should consider it.
Once done, you could handle the comparison matter by adding in the Answer base class a logic method : boolean isSubmitedAnswerMatch() and you could leave the detail implementation (String or List<String> for answers) in the constructor of the subclasses.
In this way, the Answer base class keeps a high level of abstraction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With