答案对象和面向对象的设计

时间:2018-01-25 09:13:16

标签: java oop design-patterns

我对这个设计有疑问:

public abstract class Answer {

    private long id;

    private Question question;

    public Answer(Question question) {
        this.question = question;
    }

   abstract List<String> getAnswers(){
   }
}

子类:

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对象可以有多个答案(如复选框),而SingleAnswer对象只能有单个答案(如开放式问题的答案,或单个选择a,b,c,d问题)。

现在,我想获取Answer对象列表并找出他们有什么答案,并将它们用于比较。我为了目的而制作抽象方法List getAnswers(),但我怀疑这是否是一个好的设计,因为SingleAnswer只能容纳一个值/答案,并且方法getAnswers的签名表明SingleAnswer可以返回多个答案。

这是一个好的解决方案,还是可以用不同的方式解决?

更新

我正在编写一些可以在网站上找到的问题映射器(如在线问卷,工作机会网站等),我希望将其保存在数据库中。然后当用户回答这个问题时,应用程序应在网站上填写表格并自动发送。

因此,当用户回答问题时,我需要将这些问题从数据库映射到网站上的表单,这就是为什么我想使用方法ListgetAnswers()在网站上使用Answer对象中的适当值填充表单。

以下是一些代码:

   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());
            }

}     }

以下是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;
    }

这是Question对象。我使用方法答案答案(字符串...答案)来创建和验证答案。

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;
    }
}

我想知道,如果方法getAnswers()为SingleAnswer返回size = 1的答案列表是好的吗?因为从阅读方法签名我们可以认为SingleAnswer可以返回多个答案。或者也许我对此过分思考?

3 个答案:

答案 0 :(得分:2)

在抽象基类中定义它:

abstract List<String> getAnswers();

本身并不好或坏 根据您的需要:

  

现在,我想获取答案对象列表列表并找出答案   他们有并使用它们进行比较。

我认为你应该问自己的问题是如何在提交的答案和预期的答案之间进行比较,无论Answer子类是什么。
首先,你的模型没有区分提交和预期的答案。
所以你应该考虑它。
完成后,您可以通过在Answer基类中添加逻辑方法boolean isSubmitedAnswerMatch()来处理比较问题,您可以保留详细实现(StringList<String>以获得答案)在子类的构造函数中 通过这种方式,Answer基类保持高级抽象。

答案 1 :(得分:2)

我认为你的疑虑是有道理的,它似乎不是一个好的设计。

主要问题似乎是Answer没有隐藏其详细信息,因此您必须找到一些可以容纳您需要的两种类型的接口(List<String>)。

解决方案是考虑使用Answer的内容!隐藏详细信息(字符串,无论它们是什么)并发布该功能。例如(只是猜测):

public interface Answer {
    void solve(Question q);
}

此外,您的代码中存在轻微的混淆,以及&#34;回答&#34;真的是。您有一个名为Answer的课程,但您也会拨打String个答案,并List<String>回答 s 。那么什么是&#34;答案&#34;,是Answer还是String

答案 2 :(得分:1)

我会将逻辑放在Question对象中,因为问题应该知道必须如何回答。所以,我会用每个案例的额外“验证”来装饰它(简单的文本答案,多项选择和单一限制)。像下面的对象:

import java.util.*;

public interface Question {
    Question accept(Collection<String> answers) throws InvalidAnswerException;

    final class InvalidAnswerException extends Exception {
        public InvalidAnswerException(String message) {
            super(message);
        }
    }

    final class BasicQuestion implements Question {
        private final List<String> answers = new ArrayList<>();

        @Override
        public Question accept(Collection<String> answers) {
            // do something for example:....
            this.answers.addAll(answers);
            return this;
        }
    }

    final class MultipleChoiceQuestion implements Question {
        private final Question question;
        private final Collection<String> choices;

        public MultipleChoiceQuestion(Question question, Collection<String> choices) {
            this.question = question;
            this.choices = choices;
        }

        @Override
        public Question accept(Collection<String> answers) throws InvalidAnswerException {
            if (!choices.containsAll(answers)) {
                throw new InvalidAnswerException("Please select a valid multiple choice answer.");
            }
            return question.accept(answers);
        }
    }

    final class SingleOnlyQuestion implements Question {
        private final Question question;

        public SingleOnlyQuestion(Question question) {
            this.question = question;
        }

        @Override
        public Question accept(Collection<String> answers) throws InvalidAnswerException {
            if (answers.size() > 1) {
                throw new InvalidAnswerException("You should give only one answer.");
            }
            return question.accept(answers);
        }
    }
}

因此,您可以将它们组合起来创建如下所示的任何类型的问题:

    public void test() throws InvalidAnswerException {
        Question simpleTextQuestion = new SingleOnlyQuestion(new BasicQuestion());

        Question multipleChoice = new MultipleChoiceQuestion(
            new BasicQuestion(),
            Arrays.asList("Answer 1", "Answer 2", "Answer 3", "Answer 4")
        );

        Question singleOnlyMultipleChoice = new SingleOnlyQuestion(
            new MultipleChoiceQuestion(
                new BasicQuestion(),
                Arrays.asList("Answer 1", "Answer 2", "Answer 3", "Answer 4")
            )
        );
    }

<强>更新

关于您的更新,界面很简单,通用,包含所有可能的行为,因此返回List很好。因此,SingleAnswer总是可以返回一个。这是OOP中不同行为的重点。