3 votes

Comment définir les cardinalités dans un champ Java ?

Supposons que nous ayons la classe suivante :

public class MyClass{
    List<String> list;

    void method() {
    }
}

Chaque objet de cette classe contient une liste de chaînes de caractères, mais que faire si nous voulons définir la cardinalité ? Par exemple, je veux forcer cette classe à avoir au moins 2 chaînes dans cette liste.

Existe-t-il un modèle général pour représenter les cardinalités sur les champs ?

3voto

Andy Turner Points 13883

Il suffit de s'assurer qu'il y a au moins 2 éléments dans la rubrique list par un moyen ou un autre. Il n'existe pas de méthode standard ou simple pour y parvenir.

C'est ce qu'on appelle un invariant de la classe. Il est de votre responsabilité, en tant que personne qui écrit la classe, de vous assurer que cet invariant est préservé à tout moment. En particulier :

  • Vous devez documenter l'invariant selon lequel il y a au moins 2 éléments dans la liste.

  • Vous devez vous assurer que le list contient au moins deux éléments au moment où le constructeur se termine. L'instance doit être "valide" (dans le sens où ses invariants documentés sont vrais) lorsque le constructeur se termine.

  • Vous devez vous assurer que tout le code de la classe respecte l'invariant lorsqu'il manipule la liste - vous êtes autorisé à la rendre temporairement invalide, mais vous devez vous assurer qu'il n'est pas possible d'observer votre instance dans un état invalide.

    Dans le cas d'une utilisation monotâche, cela signifie simplement que l'invariant doit être vrai une fois que la méthode publique est retournée (notez que cela s'applique également en cas d'exception) ; mais si votre code est conçu pour être utilisé de manière multithread, vous devez également vous assurer qu'aucun thread ne peut jamais voir votre instance dans un état où l'invariant est faux (par exemple, vous pouvez avoir besoin de synchronized ou s'assurer que les mises à jour sont atomiques).

  • Vous devez vous assurer que les sous-classes de votre classe ne peuvent pas rendre l'invariant faux ; ou documenter clairement qu'il est de la responsabilité des personnes qui écrivent des sous-classes de s'assurer que l'invariant reste vrai.

    Il y a un grand article dans Java efficace 2e édition à ce sujet : "Concevoir et documenter pour l'héritage ou l'interdire".

  • Vous devez vous assurer que rien en dehors de votre classe ne peut accéder à la référence à la liste. Par exemple, votre code rend la liste visible à d'autres classes du même paquetage - n'importe laquelle de ces classes pourrait appeler theInstance.list.clear() et d'invalider votre invariant.

    Il est assez difficile d'empêcher cela de manière absolue - par exemple, il pourrait être possible pour des acteurs malveillants d'invalider l'invariant en utilisant la réflexion. Vous pouvez empêcher cela, mais il s'agit de peser l'effort d'identification et de blocage de telles méthodes par rapport au coût réel de l'invariant devenant faux (cela dépend fortement de la façon dont cette classe est utilisée).

La manière la plus simple d'appliquer un invariant est, de loin, de le faire sur un fichier classe immuable . S'il n'est pas possible de modifier l'état observable d'une instance, il n'est pas possible d'invalider l'invariant. Dans ce cas, tout ce dont vous devez vous soucier est a) de vous assurer que la classe est immuable ; b) de vous assurer que l'invariant est vrai une fois que le constructeur revient. Tous les autres points ci-dessus tombent alors simplement à l'eau.

1voto

Stephen C Points 255558

Existe-t-il un modèle général pour représenter les cardinalités sur les champs ?

Bien entendu, vous pouvez représenter les cardinalités à l'aide de champs entiers, soit dans les objets eux-mêmes, soit au niveau méta. Mais cela ne sert pas à grand-chose si vous ne pouvez pas les appliquer.

Il n'y a pas de modèle général pour cela. La réponse de @Andy Turner fournit un bon résumé des alternatives sur l'application des cardinalités. Je voudrais juste ajouter quelques points :

  1. Il est peu probable que l'on parvienne à faire respecter les contraintes de cardinalité par le biais d'un typage statique. Le système de types de Java n'est pas assez riche pour le faire de manière agréable 1 .

  2. La construction d'objets dont les champs ont des cardinalités minimales peut s'avérer délicate, surtout s'il existe des circularités potentielles impliquant ces champs.

Une façon de traiter la construction est de séparer le cycle de vie des objets en une phase de "construction" et une phase "achevée". Au cours de la phase de construction, les contraintes sont relâchées afin de permettre une construction par étapes. À un moment donné, l'interrupteur "achevé" est "basculé". À ce moment-là, 1) les contraintes de cardinalité sont vérifiées et 2) le comportement des opérations de mutation est modifié pour empêcher les changements qui violeraient la cardinalité.

Cela peut être mis en œuvre à l'aide de constructeurs publics et d'une méthode publique permettant d'actionner l'interrupteur. Vous pouvez également utiliser la méthode modèle de construction ; par exemple

  • rendre les constructeurs privés
  • utiliser des moyens privés alternatifs pour contourner les cardinalités tout en construisant
  • vérifier les cardinalités et basculer l'interrupteur (s'il y en a un) dans l'espace de travail. build() méthode.

Une autre approche consiste à permettre pour qu'ils soient inférieurs à la cardinalité, mais n'autorisent l'ajout d'éléments dans les champs que lorsqu'ils sont dans cet état. En d'autres termes, il s'agit de l'approche "basculer l'interrupteur" sans interrupteur explicite. L'inconvénient est qu'un client doit tester si la contrainte de cardinalité est déjà en vigueur, c'est-à-dire si la contrainte est faible.


1 - En théorie, vous pourriez mettre en place un ListOfTwoOrMore mais cela entraînerait une série de nouveaux problèmes.

0voto

Jasper-M Points 1453

Une façon d'y parvenir est d'utiliser un type différent pour le champ. Il a maintenant le type List<String> qui est une collection pouvant contenir 0 ou plusieurs éléments. Vous pouvez le remplacer par un type qui représente une liste contenant 2 éléments ou plus.

0voto

selimssevgi Points 166

Vous pouvez utiliser des var-args dans le constructeur.

public MyClass(String s1, String s2, String... rest){
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X