Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Multiplication des interfaces ou usage immodéré des UnsupportedOperationException

7 réponses
Avatar
MattBan
Bonjour.

Je suis tr=E8s fier de mon titre. Je vous explique mon probl=E8me. J'=E9cris
des interfaces et des classes, et certaines classes n'impl=E9mentent
certaines fonctionnalit=E9s de mes interfaces. Dois-je donc ajouter des
interfaces pour ces classes ou autoriser que mon impl=E9mentation
balance un UnsupportedOperationException.

Explication sur un exemple concret.
Le package java.util contient une interface Iterator qui comprend une
m=E9thode 'remove()'. Cette m=E9thode n'est cependant pas autoris=E9e par
toutes les impl=E9mentations. Voir la doc :
=AB
Throws:
UnsupportedOperationException - if the remove operation is not
supported by this Iterator.
=BB
Mais moi, je trouve =E7a super inqui=E9tant. =C7a veut dire que si j'utilise
un it=E9rateur sur une collection g=E9n=E9r=E9(e) par une impl=E9mentation
tierce, je dois (dans le meilleur des cas) regarder la doc pour
v=E9rifier si la m=E9thode est autoris=E9e, et dans le pire des cas croiser
les doigts. On pourra me r=E9pondre que le fait d'avoir le droit de
supprimer l'=E9l=E9ment d=E9rive logiquement de la d=E9finition de la class=
e,
mais je ne suis pas d'accord et puis =E7a n=E9cessite de toute fa=E7on une
gymnastique intellectuelle non n=E9gligeable. Bref, je trouve =E7a peu
pratique.

L'autre approche serait de cr=E9er une interface UnmodifiableIterator,
=E9tendue par Iterator, qui ne requiert que l'impl=E9mentation de next()
et hasNext(). En plus, =E7a permet de d=E9grader facilement les
possibilit=E9s de l'utilisateur de votre code puisque vous pouvez
renvoyer un UnmodifiableIterator qui est en fait un Iterator, alors
que dans l'impl=E9mentation actuelle il faut passer par
Collections.unmodifiableCollection(Collection c).iterator(). Je vois
bien les probl=E8mes de cette seconde approche : on cr=E9e un grand nombre
d'interface suppl=E9mentaires, notamment si on a un grand nombre de
m=E9thodes qui peuvent ind=E9pendamment =EAtre support=E9es ou pas.

Quel est votre avis sur le sujet ? Faut-il privil=E9gier la seconde
approche quand on a peu de m=E9thodes qui posent probl=E8me ?

7 réponses

Avatar
Black Myst
Bonjour.

(...)

Quel est votre avis sur le sujet ? Faut-il privilégier la seconde
approche quand on a peu de méthodes qui posent problème ?



Je pense que dans ce cas précis, tu aborde le problème par le mauvais
coté. Pour ce qui est de la gestion des collections en Java, on peut
dire que dans 99% des cas, le développeur à besoin d'une liste modifiable.

En fait, je ne vois qu'un seul cas ou on veut rendre une liste immuable,
c'est lorsque l'on fait de la programmation défensive, pour ne pas
exposer la représentation interne de notre classe à l'extérieur et que
l'on souhaite éviter le clone pour des questions de performance.

Dans le cas de programmation défensive, il suffira de préciser dans la
doc que la liste renvoyée est immuable. Par défaut on considérera toutes
collection mutable.

Pour en revenir à ta question, je dirais que que cela dépends du cas
général. Si ton interface définie une méthode suffisamment particulières
et que tu sais pertinemment que la plupart des impléms ne pourront pas y
répondre, alors il faut peut-être envisager de découper en plusieurs
interfaces.

De l'autre coté, il arrive parfois d'avoir des solutions optimisés pour
implémenter ton interface, mais qui rendes certaine action impossible.
Dans ce cas, un bon commentaire et un UnsupportedOperationException
peuvent être une solution tout à fait pertinente.

Une dernière possibilité peut être de donner le moyen à
l'implémentation, au travers de l'interface, de dire qi elle est capable
de gérer certaine fonctionnalité

interface UsersManager {
User[] getAllUsers();
boolean acceptUser(User, Pass);
boolean canUpdatePassword()
void upatePassword(User, old, new)
boolean canRemoveUser()
void removeUser(User)
}

A++
Black Myst

Avatar
Hervé AGNOUX
MattBan wrote:


Quel est votre avis sur le sujet ? Faut-il privilégier la seconde
approche quand on a peu de méthodes qui posent problème ?


Je suis tout à fait d'accord avec toi, et regrette également l'apparition de
méthodes susceptibles d'envoyer cet "unsupported" dans le jdk.

Moi je l'utilise lorsque je ne sais pas très bien comment définir une
interface ou une classe, avec toute sa parentée ; j'y mets alors des
Unsupported, en me disant que si je tombe dessus, je l'écrirai. Netbeans
fonctionne aussi quelque fois comme ça, quand on lui demande de générer des
classes automatiquement, il remplit le bloc avec un Unsupported ; je trouve
ça bien.

Je l'utilise donc plutôt pour faciliter le refactoring. Normalement, à la
livraison, il n'y en a plus :-)


--
Hervé AGNOUX
http://www.diaam-informatique.com

Avatar
Maxime Daniel
On 8 juin, 20:34, Hervé AGNOUX
informatique.com.zz> wrote:
MattBan wrote:

Quel est votre avis sur le sujet ? Faut-il privilégier la seconde
approche quand on a peu de méthodes qui posent problème ?


Je suis tout à fait d'accord avec toi, et regrette également l'appari tion de
méthodes susceptibles d'envoyer cet "unsupported" dans le jdk.
Hervé, je crois pour ma part que cette introduction vient de la

volonté de faire un arbitrage assez fin entre le design propre et bien
général du besoin, qui introduit les "méthodes naturelles" sur
l'interface considérée, et une implémentation partielle possible pour
assurer l'implémentation d'une qualité de service différente (on
sacrifie alors l'implémentation complète de l'interface pour fournir
une classe plus rapide ou moins gourmande en mémoire ou les deux).
L'emploi de ce pattern avec cet objectif nécessite à mon avis une
grande maturation du design, beaucoup de recul, et ne devrait pas être
envisagé sans être prêt à itérer sur la conception. C'est d'une
certaine façon une approche qui, a posteriori, retire des
implémentations de méthodes jugées trop dispendieuses.


Avatar
Hervé AGNOUX
Maxime Daniel wrote:

Hervé, je crois pour ma part que cette introduction vient de la
volonté de faire un arbitrage assez fin entre le design propre et bien
général du besoin, qui introduit les "méthodes naturelles" sur
l'interface considérée, et une implémentation partielle possible pour
assurer l'implémentation d'une qualité de service différente (on
sacrifie alors l'implémentation complète de l'interface pour fournir
une classe plus rapide ou moins gourmande en mémoire ou les deux).
L'emploi de ce pattern avec cet objectif nécessite à mon avis une
grande maturation du design, beaucoup de recul, et ne devrait pas être
envisagé sans être prêt à itérer sur la conception. C'est d'une
certaine façon une approche qui, a posteriori, retire des
implémentations de méthodes jugées trop dispendieuses.


Oui, il faut faire des compromis. Mais, là où tu parles de "arbitrage assez
fin", je parlerais, moi, de "arbitrage assez grossier" :-)

Il est vrai que c'est une technique pour définir les interfaces : on y met
tous les possibles, prétendument assuré d'avoir ainsi réponse à tout. Je
n'aime pas beaucoup cette approche, y préférant celle qui consiste à
proposer une organisation de ces possibles, organisation qui prend le
risque d'être mauvaise.

J'admets que le paquetage des collections est plutôt dans la seconde
approche (proposer une organisation) que dans la première, donc c'est bon.


--
Hervé AGNOUX
http://www.diaam-informatique.com

Avatar
Maxime Daniel
On 9 juin, 12:03, Hervé AGNOUX
informatique.com.zz> wrote:
...
Oui, il faut faire des compromis. Mais, là où tu parles de "arbitrage assez
fin", je parlerais, moi, de "arbitrage assez grossier" :-)
...

Je viens de relire la classe Iterator de Java 1.5 et je maintiens le
'assez fin'. Iterator a les méthodes hasNext, next et remove. hasNext
et next constituent un noyau dur de la fonctionnalité (on pourrait se
passer de hasNext, mais le pattern classique propose hasNext et les
développeurs qui adhèrent aux thèses de Meyer sur les prérequis
préfèrent n'appeler next qu'à coup sûr, plutôt que de traiter une
exception). remove est très désirable par une prolongation naturelle
du design minimaliste. A posteriori cependant, on se rend compte que
les implémentations les plus efficaces pour certaines structures de
données sont incapables de fournir remove, et on décide donc de le
rendre facultatif. L'alternative est alors de créer une interface
héritière MutableIterator et de supprimer remove de Iterator, ou bien
de trouver une astuce pour autoriser certaines implémentations à ne
pas implémenter remove. La décision qui a été prise (seconde approc he)
s'appuie sur des jugements de valeur quant à l'équilibre général de
l'API, son nombre de classes et d'interfaces, la granularité des
interfaces les plus utilisées, etc. Elle n'est forcément pas exempte
de reproches, mais résulte de l'examen poussé de notions subtiles.
C'est donc un arbitrage 'assez fin'...

Avatar
Hervé AGNOUX
Maxime Daniel wrote:

interfaces les plus utilisées, etc. Elle n'est forcément pas exempte
de reproches, mais résulte de l'examen poussé de notions subtiles.
C'est donc un arbitrage 'assez fin'...


Je n'ai pas accès à toutes les considérations des équilibres généraux des
apis etc. L'équilibre général du paquetage des collections est assez bien
fait, donc la méthode employée pour y parvenir a des chances d'être bonne.

Pour ce qui concerne le remove, j'observe que, la plupart du temps, je ne
l'emploie pas. (je crois pouvoir dire que je ne l'emploie jamais). De plus,
comme principe général, je ne modifie pas une liste en même temps que je la
parcours.

Donc, pour moi, ce remove est maladroit, parce qu'il facilite une forme de
traitement qui me parait mauvaise.

Cependant il existe des exceptions, bien sûr, heureusement. Il y a des
listes qu'il vaut mieux modifier directement pendant le parcours. Il s'agit
souvent de listes où l'accés est distant. Par exemple, JDBC donne la
possibilité de modifier un curseur.

Ces listes sont cependant très particulières, dans leur mode d'accés, et il
me semble qu'il aurait été bon d'en approfondir les caractéristiques, pour
mieux les décrire et les utiliser, au lieu de placer un simple "remove".

D'ailleurs, le ResultSet, qui parcourt des listes de base de données, cas
dans lequel je peux concevoir que le remove (ici "deleteRow") soit
justifié, n'est pas relié au paquetage des collections.

Bug dans l'arbitrage fin ?...



--
Hervé AGNOUX
http://www.diaam-informatique.com

Avatar
MattBan
Bonjour.

Merci pour vos explications. :)

--
ban