[Squeak-fr] Equivalent du switch

Jean-François LEFEVRE jflefevre at gmail.com
Dim 28 Sep 13:35:35 UTC 2008


Bonjour,

Un dictionnaire mais seulement si il est déclaré une fois pour toute.
Surtout pas un dictionnaire créé et renseigné dans la méthode juste avant
son utilisation (trop lourd).

En regardant d'un peu plus près l'exemple de la direction on se rend compte
qu'on veut passer d'une représentation d'un concept sur un autre concept ou
sur une autre représentation. Ici, on veut passer d'une représentation du
concept de direction sous forme de symbole vers une représentation sous
forme de Point (ou plutôt de Vecteur).

Le réflexe qu'on doit avoir selon moi c'est de ce demander si on n'est pas
passé à coté d'un objet. Effectivement, ici on manipule un concept de
Direction qui n'est pas clairement défini en tant qu'objet. Il est donc
probable que mon code serait plus "Orienté Objet" avec une classe Direction.
Dans notre cas présent, il semble qu'on gère 4 directions particulières
(haut, bas, droite et gauche). Il semble donc intéressant de spécifier 4
instances particulières, constantes, qu'on placera certainement dans des
variables de classe ou dans des variables d'instances de classes. Peu
importe la manière de conserver ces instances, il estprobable que j'aurai
envie de proposer un accès à ces instances et donc je placerai des méthodes
de classe comme haut, bas, droite et gauche dans ma classe Direction.

Que sait faire un objet Direction ? Certainement un tas de choses comme me
donner un vecteur sous forme d'un Point (la méthode vector dont parle Damien
dans un message précédent). On peut se contenter dans un premier temps de
conserver cette information dans un attribut d'instance propre à chaque
instance de Direction.
Si le comportement des objets Direction devait vraiment être très différent
selon l'instance, on pourrait effectivement envisager de faire des sous
classes pour chaque direction mais on n'a peut être pas besoin d'aller
jusque là pour cet exemple (faire des hiérarchies de classes complique
souvent les choses).

Est-ce que cela élimine tous besoin d'un switch ? la réponse est
malheureusement non.
Il y a forcément un moment où je vais devoir récupérer une instance et où je
vais me demander laquelle. Par exemple si je dois récupérer l'instance
correspondant à un caractère frappé au clavier ou si je veux implémenté une
méthode comme #normal donnant la direction à 90 degrés. Une possibilité est
d'ajouter une information dans la classe Direction et d'offrir accès à la
liste des instances (ici des quatres constantes). Ainsi, je peux faire une
recherche dans cette collection. Par exemple, on peut ajouter l'information
#name et donc faire une boucle de recherche par nom sur cette collection. Si
ce besoin est fréquent, on va certainement offrir une méthode d'accès
effectuant cette recherche, ce qui pourrait donner :

directionNamed: aSymbol
    "answer the constant named aSymbol or raise an exception"

    ^self directions detect: [:each | each name = aSymbol]

Evidemment, il est tentant de faire un #perform: (à déconseiller vivement
car on ne contrôle plus ce qui se passe avec un symbole quelconque)
directionNamed: aSymbol
    "answer the constant named aSymbol or raise an exception"

    ^self perform: aSymbol

Ca n'est pas toujours souhaitable d'enrichir la classe de cette manière. Il
y a toujours des cas où on retombe sur la question "Passer d'un concept à un
autre".

Ici l'exemple de la méthode #normal commencerait à devenir limite (on va pas
ajouter à chaque instance un attribut pour donner sa #normal et aussi sa
transposé ou son inverse).

Si on reste dans le cadre des quatres constantes on va donc se faire une
séquence de if du genre :
normal
    "answer the direction rotated 90 deg"

    self == self class haut ifTrue: [^self gauche].
    self == self class gauche ifTrue: [^self bas].
    self == self class bas ifTrue: [^self droite].
    self == self class droite ifTrue: [^self haut].
    self error: 'You have created other Direction instance !!!'

Remarque : on pourrait évidemment supporter des instances constantes et non
constantes.

Dans ce cas précis on pourrait utiliser un dictionnaire (stocké quelque part
dans la classe) et remplaçant la séquence de tests par un accès plus
performant (à mesurer)
ça pourrait donc donner :
normal
    "answer the direction rotated 90 deg"

    ^self class normalsDictionary
        at: self
        ifAbsent: [self error: 'You have created other Direction instance
!!!']

Que dire de plus ? Expliquer l'intérêt de toute cette prise de tête alors
qu'un bon switch semblerait plus simple.
Stéphane donne la réponse. Que se passe-t-il si on veut faire évoluer, par
exemple en offrant maintenant les 4 directions supplémentaires hautDroite,
hautGauche, basDroite et basGauche.
L'objectif de tout ça est de diminuer les impacts sur le code utilisant la
classe et si possible de faire en sorte que les impacts ne soient que des
ajouts (de classes ou de méthodes) et non des modifications de méthodes
existantes.

Faire des switch dans son code client implique de prendre en compte les
nouveaux cas.
L'exemple de départ était la méthode
vitesseVers: dir
   dir==#Haut ifTrue [vitesse := 0 at 1].
   dir==#Bas ifTrue [vitesse := 0 at -1].
   dir==#Gauche ifTrue [vitesse := -1@].
   dir==#Droite ifTrue [vitesse := 0 at -1].

On voit bien que la prise en compte de quatre nouvelles directions impose de
modifier cette méthode et certainement tout un tas d'autre.

Proposer des alternatives (dictionnaires par exemple) avec des vrais objets
permet de déporter ces switch dans un unique endroit (la classe Direction).
Ca devient la responsabilité du développeur de cette classe de maintenir la
cohérence des dictionnaires (ici le normalsDictionary par exemple) et non
celle du client de se maintenir à jour.

Bon, je voulais mettre mon grain de sel et voilà que j'ai versé tout le
paquet. Désolé pour la longueur

Jean-François

Le 28 septembre 2008 13:47, stephane ducasse <stephane.ducasse at free.fr> a
écrit :

> un dictionaire cela veut dire que l'on peut etendre le comportement sans
> copier copier et modifier tous les cases.
>
> stef
>
> On Sep 28, 2008, at 1:33 PM, Stéphane Rollandin wrote:
>
>  Damien Pollet a écrit :
>>
>>> 2008/8/26 Stéphane Rollandin <hepta at zogotounga.net>:
>>>
>>>> sans doute, mais c'est la seule réponse techniquement correcte à la
>>>> question
>>>> posée: quel est l'équivalent du switch.
>>>>
>>> le switch c'est le message lookup :)
>>>
>>
>> non, pas du point de vue pratique. utiliser un dictionaire, ok (mais en
>> fin de compte quelle est la différence conceptuelle entre implémenter un
>> lookup via une classe, dictionaire, et une méthode, caseOf: ? pourquoi l'un
>> sortirait des clous "c'est de l'objet" et pas l'autre ?)
>>
>> en revanche, implémenter un message pour #haut, #bas, etc... ça ce serait
>> vraiment du mauvais design objet AMHA.
>>
>> Stef
>>
>> _______________________________________________
>> Squeak-fr mailing list
>> Squeak-fr at lists.squeakfoundation.org
>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/squeak-fr
>>
>>
> _______________________________________________
> Squeak-fr mailing list
> Squeak-fr at lists.squeakfoundation.org
> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/squeak-fr
>
-------------- section suivante --------------
Une pièce jointe HTML a été enlevée...
URL: http://lists.squeakfoundation.org/pipermail/squeak-fr/attachments/20080928/51b255c7/attachment-0001.htm


Plus d'informations sur la liste de diffusion Squeak-fr