2012-07-01 5 views
1

J'ai un problème de conception:Le champ doit être secret mais la sous-classe doit remplacer la méthode utilisant ce champ?

  • classe Question: une question et la réponse (String)
  • classe TextQuestion: question normale texte et répondre
  • classe NumberQuestion: question (String), réponse (nombre - int, float ...), et vérifier la réponse en utilisant une valeur epsilon

Mais j'ai un problème avec NumberQuestion. Je dois prendre la bonne réponse, la convertir en nombre, et la comparer à la réponse de l'utilisateur. Alors, comment puis-je obtenir la réponse? L'utilisation de la méthode getter n'est pas sécurisée. Ou mon design n'est pas bon? Comment puis-je changer?

public abstract class Question { 
    private String question; 
    private String answer; 

    public Question(String question, String answer) { 
     this.question = question; 
     this.answer = answer; 
    } 

    public boolean checkAnswer(String yourAnswer) { 
     // default implementation 
    } 
} 

class TextQuestion extends Question { 
    // Answer and Question is always string type, it's OK, not problem 
} 

class NumberQuestion extends Question { 
    // Question is String, OK 
    // Answer: input is number, accept approximately 
    // Ex: Question: 1/3 = ? 
    //  Answer: 0.3 or 0.33 or 0.333 are accepted (using an Epsilon value) 

    // so must override checkAnswer 
    public boolean checkAnswer(String yourAnswer) { 
     // HOW can I do? 
    } 
} 
+1

Que signifient par « pas sûr »? Quel genre de risque essayez-vous d'éviter? –

+0

@TedHopp: cela signifie que toutes les classes (sauf ses sous-classes) peuvent obtenir la réponse. –

Répondre

4

Je ne vois pas pourquoi vous devez cacher la réponse des sous-classes (ou de n'importe quel code client, d'ailleurs). Le code client doit sûrement connaître la réponse en premier lieu afin de créer une instance TextQuestion ou NumberQuestion.

Vous devriez abstraire la logique du test, ainsi que le type de réponse, de la classe Question:

public abstract class Question<AnswerType> { 
    private String question; 
    private AnswerType answer; 

    public Question(String question, AnswerType answer) { 
    this.question = question; 
    this.answer = answer; 
    } 

    public abstract boolean checkAnswer(AnswerType yourAnswer); 

    protected AnswerType getAnswer() { 
    return answer; 
    } 
} 

class TextQuestion extends Question<String> { 
    public TextQuestion(String question, String answer) { 
    super(question, answer); 
    } 

    public boolean checkAnswer(String yourAnswer) { 
    return answer.equals(yourAnswer); 
    } 
} 

class NumberQuestion extends Question<Double> { 
    private double epsilon; 

    public NumberQuestion(String question, Double answer, double epsilon) { 
    super(question, answer); 
    this.epsilon = epsilon; 
    } 

    public boolean checkAnswer(Double yourAnswer) { 
    return Math.abs(getAnswer() - yourAnswer) < epsilon; 
    } 
} 
+0

OK, merci pour votre réponse. Je souhaite que la classe Question masque la réponse du code des clients, et non des sous-classes. Je me demande pourquoi le mot-clé protected permet à toutes les classes du même paquet d'accéder à ce champ (ou méthode). –

0

Je ne sais pas si vous êtes familier avec les classes génériques, mais ce serait approprié dans ce cas. Vous pouvez juste le google. :-)

Si vous ne voulez pas faire cela, vous pouvez toujours convertir à la fois yourAnswer et this.answer dans le même type, alors vous pouvez trouver la différence absolue et vous assurer qu'ils sont inférieurs à un seuil spécifique.

+0

OP veut garder secrète 'answer' des sous-classes pour une raison quelconque. –

1

Si votre réponse peut avoir différents types, vous pouvez également utiliser des génériques dans votre classe abstraite.

public abstract class Question<T> { 
    private final String question; 
    protected final T answer; 

    public Question(String question, T answer) { 
     this.question = question; 
     this.answer = answer; 
    } 

    //Default implementation 
    public boolean checkAnswer(T yourAnswer) { 
     return answer.equals(yourAnswer); 
    } 
} 

Et lorsque vous définissez la sous-classe:

class NumberQuestion extends Question<BigDecimal> { 
    ... 
    private final BigDecimal epsilon; 

    public NumericQuestion(String question, BigDecimal answer, BigDecimal epsilon) { 
     super(question, answer); 

     this.epsilon = epsilon; 
    } 

    @Override 
    public boolean checkAnswer(BigDecimal yourAnswer) { 
     BigDecimal result = this.answer.subtract(yourAnswer).abs(); 

     return (result.compareTo(epsilon) == -1); // check whether result < epsilon 
    } 
} 

Par ailleurs, au lieu d'une double préférable d'utiliser BigDecimal pour les calculs en virgule flottante. Il y a beaucoup de bons articles à ce sujet. Vous pouvez "google" cette question si cela vous intéresse.

Si vous devez définir l'échelle pour les nombres à virgule flottante, vous pouvez facilement le faire avec une grande décimale, par exemple:

result = result.setScale(5); 

Et attention parce que la classe BigDecimal est immuable (pas complètement, mais cela est une autre question) et renvoie une nouvelle instance lorsque vous essayez de changer son état.

+0

OP veut utiliser quelque chose comme 'Math.abs (answer-yourAnswer)

+0

Oui. Mais il peut facilement changer l'implémentation par défaut dans la sous-classe. – dimas

+1

@dimas: mais comment puis-je vérifier la réponse dans NumberQuestion? yourAnswer comparez à quoi? –

Questions connexes