Ohhh, il y a tant à dire sur ton article, je vais encore une fois exploser la base de données
Première ineptie :
Disons-le d’emblée, Déméter est un leurre ! Une curiosité tout au plus.
Non !!! Déméter nous permet de nous assurer que tous les services rendus par un type de donnée particulier (par une abstraction particulière) éviteront à l'utilisateur de celui-ci (de celle-ci) de faire des erreurs lorsqu'il manipulera une donnée de ce type.
Ce n'est pas un leurre, c'est une réalité : les services fournis par une abstraction donnée doivent s'assurer que la donnée qui réagit à l'ordre qu'on lui donne sera dans un état cohérent après avoir réagi à cet ordre si elle était dans un état cohérent avant que l'ordre ne soit donnée.
Bien sur, si la donnée n'était déjà pas dans un état cohérent avant que l'ordre ne soit donnée, on ne peut plus rien, mais c'est sans doute justement que Déméter n'a pas été respecté "ailleurs" et qu'on a laissé à l'utilisateur de notre type de donnée une "chance de faire une connerie"; chaque que l'utilisateur se sera empressé d'attraper en vertu de la loi de finagle
deuxième ineptie :
Avec Déméter, l’
ensemble des services offerts par les composantes de la classe Agent doivent être répliqués au niveau de celle-ci. Cela peut faire un paquet de nouvelles méthodes !
(emphasis is mine
)
Non!
Ne devront être "répliqués" au niveau d'une classe utilisatrice que les services fournis par ses composants qui on du sens au niveau de la classe utilisatrice.
Un exemple : une classe voiture qui utilise une classe réservoir. on se fout pas mal, lorsqu'on manipule une voiture de connaitre la capacité maximale du réservoir : les services que la classe voiture expose afin de pouvoir interagir avec le réservoir sont là pour s'assurer que, quoi qu'il arrive, nous ne pourrons pas dépasser la capacité maximale du réservoir.
Le réservoir dispose d'une fonction
maxCapacity() renvoyant la capacité maximale du réservoir
C'est tout à fait normal : c'est l'un des services que l'on est en droit d'attendre de la part de ce type de donnée. Et c'est aussi un des services auquel les différentes fonctions membres de la classe voiture qui ont pour but de manipuler le réservoir risque de faire appel très régulièrement.
Mais, au niveau de la voiture, l'idée est que nous n'avons absolument pas besoin que cette information soit "dévoilée" : les ordres que nous pourrons donner à notre voiture devront "simplement" veiller à renvoyer la quantité exacte de carburant qu'il nous a été possible de rajouter ou d'utiliser dans les limites admises par la capacité maximale
troisième ineptie :
Il faut bien comprendre que les composantes de la classe Agent peuvent avoir leurs propres composantes, et ainsi de suite. C’est un des avantages des types bien conçus, que de pouvoir être réutilisés facilement pour construire d’autres types. Agent::Savoir, par exemple, pourrait donner accès à une base de Faits, ainsi qu’à une liste d’Agents connus . Le principe de Déméter étant par essence récursif, ces sous-composantes vont à leur tour faire grossir le nombre de méthodes à ajouter dans Agent.
Si l'on voulait répliquer tous les services au niveau de la classe utilisatrice, tu aurais sans doute raison.
Or, comme il n'en est rien, tu as tout à fait tord sur ce point : si une classe A utilise une classe B et une classe C ; que la classe B utilise une classe D et une classe E et que la classe C utilise une classe F et une classe G sous une forme (chaque trait correspond à une relation "utilise" ou est utilisé par"
) proche de
1 2 3 4 5 6
|
A
/ \
B C
/ \ / \
D E F G |
tous les services proposés par D et par E ne sont pas forcément destinés à se retrouver dans B, et il en est de même pour les services proposés par F ou G au niveau de C. Si bien que l'aperçu que l'on peut avoir des services proposés par D, E , F ou G au travers de B ou de C est particulièrement restreint par rapport à la somme des services que les classe D, E, F et G peuvent fournir, et ce, malgré le fait que les services proposés par B puissent (ou non !!!) utiliser tous les services exposés par D ou par E et que ceux proposés par C puissent (ou non!!!) utiliser l'ensemble des services proposés par F ou par G.
De même, les services proposés par A pourrons (ou non !!!) ** peut-être ** faire appel à tous les services proposés par B ou par C, mais il n'en restera pas moins que l'on ne retrouvera pas forcément l'ensemble des services de B (ou de C) dans l'interface de A
Quatrième ineptie :
Enfin, il faut comprendre que les types de ces composantes, conçus pour représenter des concepts importants du S.M.A., ont des chances d’être réutilisés en divers autres endroits. Par exemple, la classe Comportement pourrait également servir de composante à un type Machine. La loi de Déméter implique d’effectuer le même travail d’ajout de méthodes pour la classe Machine que celui réalisé pour Agent, occasionnant une certaine redondance quand bien même ces deux classes partagent des composantes de même type.
De toutes évidences, tu n'arrive toujours pas à te défaire de l'habitude de penser en termes de données au profit d'une approche en termes de services.
ON SE FOUT PAS MAL DES DONNEES QUI PERMETTENT A UNE CLASSE DE FOURNIR LES SERVICES QU'ELLE PROPOSE. Tout ce que l'on veut, c'est qu'elle propose un certain nombre de services, un certain nombre de comportements qui soient
- cohérents par rapport au concept que l'on tente de modéliser
- en mesure de garantir la cohérence du concept à l'exécution.
Il se peut que deux concepts "complexes" utilisent en interne le même concept "plus simple", mais :
- il se peut que nos deux concepts "complexes" utilisent le concept plus simple d'une manière totalement différente et, si ce n'est pas le cas,
- la création d'une interface exposant les comportements communs à nos deux concepts "complexes" évitera tout risque de redondance
Maintenant, si on s’intéresse au code client, il est vrai qu’avant Déméter, le code dépend de la classe Agent ainsi que de ses composantes. Après Déméter, le code client ne dépend plus que de la classe Agent seule, mais étant donné que celle-ci a vu son interface publique augmenter jusqu’à inclure l’ensemble des fonctionnalités disponibles auparavant via ses composantes, on a une autre forme de dépendance.
En fait, avec une gestion dynamique des composantes de la classe Agent [penser unique_ptr], il est possible avant Déméter de ne faire voir au code client que les composantes effectivement manipulées, voire aucune le cas échéant. Avec Déméter, le code client voit systématiquement toutes les fonctionnalités possibles, même s’il n’en utilise aucune…
Encore une fois, ton hypothèse de départ étant mauvaise, toute ta thèse s'écroule comme un château de cartes
La création des nouvelles méthodes peut entraîner de sérieuses difficultés de nommage, particulièrement en cas de structure profonde.
Pourquoi donc
le nom d'une fonction doit exprimer clairement l'objectif poursuivi par la fonction.
Et rien n'empêche d'avoir deux fonctions portant le même nom (car destinées à obtenir un résultat identique) mais utilisant des paramètres différents... Cela s'appelle : la surcharge de fonctions
Si, du point de vue interne, la classe Agent conserve sa structure de composantes, du point de vue externe, le client ne voit que l’interface publique, et donc pour lui, Agent est devenue une classe monolithique, d’un abord difficile par conséquent.
Si tu pars de l'hypothèse que tous les services exposés par les classes "composantes" sont forcément exposés par la classe "utilisatrice", alors, oui, en effet...
Mais, encore une fois, ton hypothèse est fausse: tout nous incite à faire en sorte que la classe utilisatrice n'expose
QUE L'ENSEMBLE MINIMAL INDISPENSABLE DES SERVICES QUE L'ON EST DECEMMENT EN DROIT D'ATTENDRE DE LA PART DU CONCEPT MODELISE. En deux mots : respecte également les principes SOLID (dont l'ISP en priorité), et tu te rendras compte que ton hypothèse de travail vole littéralement en éclats
Finalement, et c’est le constat le plus sévère, Déméter tend à maximiser l’interface publique des classes, ce qui accroît directement la complexité globale du code…
Si l'on s'en tient à la seule loi de Déméter, oui, peut être...
Mais c'est faire preuve d'une bien mauvaise approche conceptuelle que de se limiter à l'usage de cette seule loi. Les principes SOLID sont d'égale importance par rapport à cette loi et vouloir appliquer l'un sans l'autre n'a absolument aucun sens!
Revois ta conception, applique les principes SOLID en même temps que la loi de Déméter, et tu verras que toutes tes thèses ne tiennent absolument plus!
Bon, je vais m'arrêter là, mais je ne dirais qu'une seule chose : ton approche est loin d'être assez complète et "conceptuellement cohérente" que pour que l'on puisse prendre tes conclusions au sérieux
3 |
0 |