web-dev-qa-db-fra.com

Pourquoi les collections Java Java ont-elles été implémentées avec des "méthodes facultatives" dans l'interface?

Au cours de ma première implémentation étendant le cadre de collecte Java Java, j'ai été assez surpris de voir que l'interface de collecte contient des méthodes déclarées comme facultatives. L'implémentateur devrait lever UnsupportedOperationExceptions s'il n'est pas pris en charge. Cela m'a immédiatement frappé comme un mauvais choix de conception d'API.

Après avoir lu une grande partie de l'excellent livre "Effective Java" de Joshua Bloch, et appris plus tard qu'il peut être responsable de ces décisions, il ne semble pas s'accorder avec les principes énoncés dans le livre. Je pense que déclarer deux interfaces: Collection et MutableCollection qui étend Collection avec les méthodes "facultatives" aurait conduit à un code client beaucoup plus facile à gérer.

Il y a un excellent résumé des problèmes ici .

Y avait-il une bonne raison pour laquelle des méthodes optionnelles ont été choisies au lieu de l'implémentation des deux interfaces?

69
glenviewjeff

La FAQ fournit la réponse. En bref, ils ont vu une explosion combinatoire potentielle d'interfaces nécessaires avec une vue modifiable et non modifiable, supprimée uniquement, ajoutée uniquement, de longueur fixe, immuable (pour le filetage), etc. pour chaque ensemble possible de méthodes d'options implémentées.

28
ratchet freak

Il me semble que le Interface Segregation Principle n'était pas aussi bien exploré à l'époque qu'aujourd'hui; cette façon de faire (c.-à-d. votre interface inclut toutes les opérations possibles et vous avez des méthodes "dégénérées" qui lèvent des exceptions pour celles dont vous n'avez pas besoin) était populaire avant SOLID et ISP est devenu le norme de facto pour le code de qualité.

10
Wayne Molina

Bien que certaines personnes détestent les "méthodes facultatives", elles peuvent dans de nombreux cas offrir une meilleure sémantique que les interfaces hautement séparées. Entre autres, ils permettent à un objet d'acquérir des capacités ou des caractéristiques au cours de sa vie, ou qu'un objet (en particulier un objet wrapper) ne sache pas quand il est construit les capacités exactes qu'il doit signaler.

Bien que j'appellerai à peine les classes de collection Java parangons de bonne conception, je suggérerais qu'un bon cadre de collections devrait inclure à sa fondation un grand nombre de méthodes facultatives ainsi que des moyens de demander à une collection ses caractéristiques et ses capacités. Une telle conception permettra d'utiliser une seule classe wrapper avec une grande variété de collections sans masquer accidentellement les capacités que la collection sous-jacente pourrait posséder. Si les méthodes n'étaient pas facultatives, elles le seraient il est nécessaire d'avoir une classe d'encapsuleur différente pour chaque combinaison de fonctionnalités que les collections peuvent prendre en charge, sinon certains encapsuleurs peuvent être inutilisables dans certaines situations.

Par exemple, si une collection prend en charge l'écriture d'un élément par index, ou l'ajout d'éléments à la fin, mais ne prend pas en charge l'insertion d'éléments au milieu, le code voulant l'encapsuler dans un wrapper qui consignerait toutes les actions effectuées dessus aurait besoin d'une version du wrapper de journalisation qui prévoyait la combinaison exacte des capacités prises en charge, ou si aucune n'était disponible, il faudrait utiliser un wrapper qui prend en charge l'ajout ou l'écriture par index mais pas les deux. Si, cependant, une interface de collecte unifiée fournissait les trois méthodes comme "facultatives", mais incluait ensuite des méthodes pour indiquer laquelle des méthodes facultatives serait utilisable, alors une seule classe wrapper pourrait gérer les collections qui implémentent n'importe quelle combinaison de fonctionnalités. Lorsqu'on lui a demandé quelles fonctionnalités il prend en charge, un wrapper peut simplement signaler tout ce que la collection encapsulée prend en charge.

Notez que l'existence de "capacités facultatives" peut dans certains cas permettre aux collections agrégées d'implémenter certaines fonctions de manière beaucoup plus efficace qu'il ne serait possible si les capacités étaient définies par l'existence d'implémentations. Par exemple, supposons qu'une méthode concatenate ait été utilisée pour former une collection composite à partir de deux autres, la première étant une ArrayList avec 1 000 000 d'éléments et la dernière étant une collection de vingt éléments qui ne pouvait que être itéré dès le début. Si la collection composite était demandée pour le 1 000 013e élément (indice 1 000 012), elle pourrait demander à la liste de tableaux combien d'éléments elle contenait (c.-à-d. 1 000 000), soustraire cela de l'index demandé (ce qui donne 12), lire et ignorer douze éléments du deuxième collection, puis renvoyez l'élément suivant.

Dans une telle situation, même si la collection composite n'aurait pas un moyen instantané de renvoyer un élément par index, demander à la collection composite le 1 000 013ème élément serait toujours beaucoup plus rapide que de lire 1 000 013 éléments individuellement et d'ignorer tout sauf le dernier une.

4
supercat