Howto de conception pour l'extension
Il y a un Checkstyle règle DesignForExtension. Il dit: si vous avez un public/protected méthode qui n'est pas abstrait, ni définitive, ni vide, c'est pas "conçu pour l'extension". Lire la la description de cette règle sur la page Checkstyle de la justification.
Imaginez ce cas. J'ai une classe abstraite qui définit certains champs et une méthode de validation de ces champs:
public abstract class Plant {
private String roots;
private String trunk;
//setters go here
protected void validate() {
if (roots == null) throw new IllegalArgumentException("No roots!");
if (trunk == null) throw new IllegalArgumentException("No trunk!");
}
public abstract void grow();
}
J'ai aussi une sous-classe de la Plante:
public class Tree extends Plant {
private List<String> leaves;
//setters go here
@Overrides
protected void validate() {
super.validate();
if (leaves == null) throw new IllegalArgumentException("No leaves!");
}
public void grow() {
validate();
//grow process
}
}
À la suite de la Checkstyle règle de la Plante.valider() la méthode n'est pas conçu pour l'extension. Mais comment la conception de l'extension dans ce cas?
- Vous ne devriez pas jeter une IllegalArgumentException dans une méthode qui ne prend pas d'arguments...
- Faisons semblant de croire que c'est l'exception IllegalStateException pour le bien de la "argument" 🙂
Vous devez vous connecter pour publier un commentaire.
La règle est de se plaindre car il est possible pour un dérivant (extension) de la classe de remplacer complètement les fonctionnalités dont vous avez fournis sans vous dire à ce sujet. C'est une forte indication que vous n'avez pas pleinement compte de la façon dont le type peut être étendue. Ce qu'il veut que vous faire est plutôt quelque chose comme ceci:
Remarque que maintenant quelqu'un peut encore fournir leur propre code de validation, mais ils ne peuvent pas remplacer votre pré-code écrit. Selon la façon dont vous utilisez le
validate
méthode vous pourrez également rendre public final à la place.grow
ing-il. Autre chose, quelle qu'elle soit, est la partie du contrat qui doivent être suivies lors de la mise en œuvre d'une Plante. Remarque: les outils d'analyse statique comme Checkstyle et PMD sont les plus utiles lors de la mise en œuvre de manière réfléchie et délibérée.Bien que la réponse de Joel Coehoorn explique comment surmonter le problème concret posté par l'OP, je voudrais suggérer une approche qui prend un point de vue plus large sur la " manière de la conception de l'extension?’
Comme l'OP souligne dans un de ses commentaires, la solution n'est pas de l'échelle avec un croissant (de classe) l'héritage de la profondeur. Aussi, l'anticipation dans la classe de base la nécessité de valider les éventuels enfants des classes (
validateTreeEx()
) est problématique pour des raisons évidentes.Proposition: Vérifiez les plantes des propriétés au moment de la construction et de supprimer
validate()
tout à fait (ainsi que les setters; voir aussi http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods-are-evil.html). Le code original suggère quevalidate()
est un invariant, ce qui doit être vrai avant chaquegrow()
opération. Je doute que cette conception est intentionnelle. Si aucune opération n'est effectuée, ce qui peut "casser" une plante après la construction, il n'est pas nécessaire de vérifier la validité de plus et plus de nouveau.Va encore plus loin, je serais douter de la validité de la période initiale de l'héritage de la conception. Sans supplémentaires (éventuellement polymorphes) de l'exploitation,
Tree
juste réutilise certaines propriétés de la Plante. Je tiens fermement d'avis, que l'héritage de classe ne doit pas être utilisé pour la réutilisation du code. Josh Bloch a ceci à dire (à partir de Efficace Java, 2nd Edition, chapitre 4):Également vérifier 'Article 17: la Conception et le document de l'héritage ou de l'autre de l'interdire" (également le chapitre 4 pour le même livre)