Une mauvaise compréhension et utilisation du principe DRY peut amener à un état du code catastrophique: évitez la traversée du désert.
Principe DRY
DRY (Don't Repeat Yourself) est un principe de design présenté par Andy Hunt et Dave Thomas dans leur livre The Pragmatic Programmer: From Journeyman to Master publié en 1999 :
Dans un système, toute connaissance doit avoir une représentation unique, non-ambiguë, faisant autorité.
-- Andy Hunt, Dave Thomas - The Pragmatic Programmer
Une interprétation simpliste souvent retenue consiste à dire que toute duplication de code est le mal absolu.
Outre le fait que le concept de DRY dépasse largement le périmètre du code, la notion de duplication est à contrebalancer avec la notion d'orthogonalité afin d'éviter les erreurs de design.
Les maux de la duplication
Le principe DRY est un excellent principe de développement, au sens large. L'idée est d'éviter la duplication :
- d'information, afin de limiter les problèmes liés à la synchronisation des différentes sources
- de traitement, afin de faciliter la maintenance en cas de modification
Dans leur livre, Andrew H. et David T. indique 4 façons dont la duplication peut survenir :
- Duplication imposée
- Duplication par inadvertance
- Duplication par impatience
- Duplication inter-développeurs
Duplication imposée
Un exemple parlant de duplication imposée, ou ressenti comme imposée, est celui du modèle de données. La création à la main des DTO qui représentent les tables, ou tout autres objets représentatifs de la base de données, est une duplication. La mise à jour de la base implique une mise à jour manuelle des DTO. Aujourd'hui de nombreux frameworks ont vu le jour pour éviter cette duplication. Pour ceux qui comme moi aime garder la main sur la modélisation des bases de données, une DAL minimale à base de DataReader, un projet SQL Serveur pour maîtriser la modélisation et un script T4 pour la génération des DTO font très bien l'affaire.
Un second exemple intéressant est celui de la documentation. Vous pourrez retrouver bientôt un article complet à ce sujet sur mon blog. En attendant voici un lien vers l'excellente présentation de Cyrille Martraire, auteur du livre Living Documentation.
Duplication par inadvertance
La duplication par inadvertance correspond tout simplement à une erreur de design. On peut citer comme exemple une documentation qui exprime la même notion dans deux paragraphes distincts. On peut citer également une méthode identique qui n'a pas été factorisé.
Duplication par impatience
La duplication par impatience ? La tentation du copier / coller est toujours présente lorsque la pression augmente pour livrer à temps. Les méthodes identiques ne sont pas factorisées intentionnellement, utilisation de types primitifs au lieu d'objets métiers, ... Un conseil cependant : les minutes gagnées sur le coup risque de devenir des heures de maintenance par la suite.
Duplication inter-développeurs
Enfin, la duplication inter-développeurs : deux (ou plus) développeurs, sur un même système, développent en parallèle une fonctionnalité identique.
Oui, mais...
Avez-vous déjà entendu parler des ces périmètres applicatifs appelés bounded context ? Si vous vous intéressez à Domain Driven Design, ce terme doit vous être familier.
Bounded context est un pattern d'architecture qui consiste à organiser le code d'une application par domaine métier (version courte). L'exemple donné par Martin Fowler dans son blog montre deux bounded context : un contexte "sales" et un contexte "support".
Oh mon dieu : les entités Customer et Product sont dupliquées !
-- Quelqu'un
Lorsque l'on essaye de modéliser un domaine très vaste, prenons le domaine d'Amazon par exemple, il devient difficile de créer un seul modèle unifié. Marketing, vente, livraison, support, facturation, comptabilité, ... chacune de ces équipes vont certainement parler de client mais auront chacune une vision très différente de cet interlocuteur. Certaines équipes vont être intéressées par des propriétés communes de leur client, d'autres équipes seront les seules intéressées par une ou plusieurs propriétés. Les équipes interagirons différemment avec le client.
Essayer de regrouper toutes ses propriétés et interactions s'avère rapidement source de complexité. Faire hériter une classe SupportCustomer d'une classe Customer me dirait-vous ? Avec cette approche les deux contextes vont se retrouver fortement couplé. Très vite vous allez vous rendre compte qu'une propriété "de base" du client n'est en fait pas exactement la même ou ne doit pas être gérée de la même façon pour SupportCustomer ou pour SalesCustomer. Pour ceux qui ont déjà eu à gérer cela, vous savez que cela commence a sentir mauvais...
Au-delà du code, si nous nous mettons dans le cadre d'une architecture orientée services, voici ce qui nous attends potentiellement :
Les données elles-mêmes vont être "dupliquées", potentiellement dans des bases de données séparées, voire de technologies différentes.
"Bon sang, mais c'est horrible !"
-- Quelqu'un
Cette duplication est-elle vraiment si horrible ?
Orthogonalité
En programmation, le concept d'orthogonalité renvoi à la notion de découplage : les modifications effectuées sur une portion de code n'affectent pas une autre portion.
Si nous reprenons notre exemple précédent, on voit bien que les contextes de Sales, Shipping ou Support sont orthogonaux.
On pourrait envisager un système un peu plus complexe dans lequel les contextes de Sales et de Shipping doivent avoir une interaction entre eux.
Dans ce cas, la mise en place du pattern hexagonal (avec ses adaptateurs) va permettre de s'assurer du découplage entre les bounded context.
Quel est le rapport avec DRY ?
L'orthogonalité est étroitement liée au principe DRY présenté à la page 27. Avec DRY, vous cherchez à minimiser la duplication au sein d'un système, tandis qu'avec l'orthogonalité, vous réduisez l'interdépendance entre les composants du système.
-- Andrew Hunt, David Thomas - The Pragmatic Programmer
Tout simplement que l'application du principe DRY est une excellente chose pour réduire la complexité d'un système. Mais poussé à l'extrême, elle peut impliquer un trop fort couplage qui va alors faire augmenter la complexité dudit système.
Conclusion
Simple dans son énoncé, le principe DRY est un peu plus compliqué qu'il n'y parait. Il doit être intimement associé au principe d'orthogonalité afin de s'assurer une flexibilité, une compréhensibilité et une maintenance optimum du code.