Suite au post très intéressant de Xavier Nopre sur le Single Responsibility Principle et son approche beans+services, Nicolas Capponi a animé une séance du dojo du club agile pour explorer différents solutions qui s'offrent à nous pour ce problème.
Personnellement j'ai choisi d'utiliser le temps pour m'affuter en Visitor, car comme dis Xavier ce n'est pas le pattern le plus facile. D'autres personnes ont essayé d'autres solutions.
Voici deux variantes du Visitor pattern. L'un qui expose les objets métier (et donc utilise des getters) l'autre qui fait passer des primitives pour résoudre ce problème (pas forcément mieux, juste différent).
Extrait de la solution avec objets métier:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// original code https://gist.github.com/3879131 | |
// and blog post by Xavier Nopre | |
// http://xnopre.blogspot.fr/2012/10/developpement-et-conception-mon.html | |
// 2 whole solutions can be found at https://github.com/martinsson/Dojo61-SR, | |
// tags: visitorWithRealObjects, visitorWithoutGetters | |
public class Cart { | |
//... | |
public void accept(CartVisitor visitor) { | |
for (Product product : products) { | |
product.accept(visitor); | |
} | |
visitor.visit(this); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Mailer implements CartVisitor { | |
private String productContent = ""; | |
private String cartContent; | |
public void visit(Product product) { | |
productContent += "- " + product.getName() + " au prix de " | |
+ product.getPrice() + "\n"; | |
} | |
public void visit(Cart cart) { | |
cartContent = "Bonjour,\nVotre panier compose le " + cart.creationDate | |
+ " comporte les elements suivants :\n"; | |
} | |
public String computeMailContent(Cart cart) { | |
cart.accept(this); | |
return cartContent + productContent; | |
} | |
} |
Les autres solutions explorés, dont j'aimerais bien voir le résultat, était délégation à une autre classe ou introduction d'une petite interface pour gérer le cas d'utilisation en question (par exemple Mailer pour contenir computeMailContent()). Ces deux solutions me semblent être des bonnes solutions temporaires en attendant de les extraire pour de bon. Par contre une fois extraite il me semble qu'on va tomber sur soit le visitor soit la solution que mentionne Xavier: beans+services. Non?
Conclusion
Les visitor qu'on trouve ici, est-ce vraiment trop complexe? Personnellement je pense que nous gagnerions à simplement pratiquer ce pattern pour savoir le mettre en oeuvre avec aise quand cela est préférable. Certes il faut le faire plusieurs fois pour avoir l'impression de maitriser, mais nous sommes nombreux à être informaticiens plus de 10 ans, alors qu'il suffit avec ~10h de pratique étalé sur quelques mois pour le maitriser en profondeur. Qu'en pensez-vous?