Introduction
En tant que développeur, il nous est souvent arrivé durant notre scolarité, de nos recherches ou bien de nos discussions interprofessionnelles d’entendre parler des « Design Patterns » (Patron de Conception).
Parfois cela permet de faire plus savant ou au pire arrogant, mais est-ce que tout le monde connait l’utilité ou bien tout simplement y pense lorsqu’il code un algorithme, une « feature » ou bien une solution face à un bug.
Alors pour les plus dissipés en cours d’informatique, les « Design Patterns » sont des solutions réutilisables pour résoudre des problèmes communs en informatique. Cela permet de créer du code plus facile à réaliser, modifier, comprendre et réutiliser.
Ce sont les meilleures solutions connues à un problème de conception récurrent.
Types de Design Patterns
Il y a une multitude de « Design Patterns » dans le milieu de la programmation informatique qui furent développés ces dernières décennies (Notamment les 23 du Gang of Four) et certains plus récents (ex : VIPER).
Mais l’ensemble de ceux-ci peuvent se classer en 3 types :
· Création (Creational)
· Structure (Structural)
· Comportement (Behavioral)
Le premier englobe ceux qui traitent de la création des objets. Le deuxième aborde le fait de structurer les classes et les objets et les liens entre eux. Et le dernier traite de la façon dont les classes ou les objets vont interagir entre eux (notion de responsabilité).
Le plus connu (et le plus mal utilisé)
Le « Design pattern » le plus connu est le Singleton. Celui-ci vise à ce qu’une et une seule instance d’une classe existe au sein du programme et que celle-ci soit accessible depuis n’importe quel endroit du programme.
Mais il est utilisé la plupart du temps pour solutionner des problèmes qui devraient pas faire appel à un Singleton.
Exemple :
Pour faire passer des informations d’une classe à une autre, on pourrait être tenter de créer un Singleton « Settings » alors que de créer un « Model » de l’une à l’autre moyennant peut-être un intermédiaire.
Cet exemple peut-être un peu léger pour justifier la non-utilisation d’un « Singleton » mais si vous rajoutez dans la balance des tests unitaires, il serait judicieux de reconsidérer l’offre !
En effet, un objet global tel un Singleton qui conserve son état et les valeurs de ses variables. L’ordre des tests devient alors un problème et un état initial peut-être pénible à obtenir après une série de tests.
Mais à titre d’exemple voici comment en réaliser un en Swift :
final class LibraryAPI {
static let shared = LibraryAPI()
private init() { }
}
Présentation de la « Factory » (Creational)
Le principe de ce « Pattern » est que la classe exacte de l’objet n’est pas connu par l’appelant.
Prenons l’exemple d’un jeu vidéo, le personnage ramasse une arme et veut tirer avec celle-ci. Il n’a pas forcément besoin de savoir quelle est cette arme du moment qu’elle tire et lui permet de tuer ses ennemis.
Cet exemple un peu bancal permet de faire le parallèle avec la « Factory » car la classe « Character » peut avoir une référence d’objet « Weapon » qui possède la méthode « Fire() ». La classe « Character » n’a pas besoin de savoir quel type d’arme il s’agit du moment que la classe s’occupant de la création des armes dans le jeu (« WeaponFactory ») lui en a fourni une. On peut par exemple rajouter des armes plus tard sans à avoir à se soucier si notre personnage sera à même de les utiliser (Il saura le faire !)
Ce « Pattern » permet de bien séparer la logique d’utilisation de l’objet de la nature même de l’objet.
Présentation de la « Facade » (Structural)
Le principe de ce « Pattern » est de cacher une conception et une interface complexe difficile à comprendre.
Le principe de ce « Pattern » est d’encapsuler la complexité des interactions entre les objets.
Prenons l’exemple d’un jeu vidéo (car on aime bien), le personnage doit courir et la course est fastidieuse. Il faut penser à chaque jambe, maintenir son équilibre, penser à respirer, etc…
Alors oui, nous le faisons naturellement, nous humains, et les designeurs font des animations qui réunissent toutes ces actions en une seule animation.
Et bien c’est exactement ça la « Facade », en faisant un « Character.Run.Play() » le personnage court sans qu’on ait à gérer toutes ces actions.
Chaque membre pourrait être une référence dans la classe « Character » (peut-être qu’il est possible de les customiser) et l’interface nous fournissant une méthode « Run() » il serait alors facile de le faire courir.
L’intérêt général est d’assainir une API / Librairie / Classe rendue compliqué par les ajouts successifs, cela facilite le test, la lecture, etc…
class UserfriendlyDate {
GregorianCalendar gcal;
public UserfriendlyDate(String isodate_ymd) {
String[] a = isodate_ymd.split("-");
gcal = new GregorianCalendar(Integer.parseInt(a[0]),
Integer.parseInt(a[1])-1 /* !!! */, Integer.parseInt(a[2]));
}
public void addDays(int days) {
gcal.add(Calendar.DAY_OF_MONTH, days);
}
public String toString() {
return String.format("%1$tY-%1$tm-%1$td", gcal);
}
}
class FacadePattern {
public static void main(String[] args) {
UserfriendlyDate d = new UserfriendlyDate("1980-08-20");
System.out.println("Date : "+d);
d.addDays(20);
System.out.println("20 jours après : "+d);
}
}
ja
Conclusion
Les « Design Patterns » font partie intégrante du développement actuellement. Que ce soit conscient ou inconscient, nous les utilisons quotidiennement (MVC, Singleton, Facade, Observer, etc…).
Certains sont très connus, d’autres moins mais il serait intéressant de s’intéresser à ceux que nous connaissons pas ou peu pour pouvoir anticiper des nouveaux problèmes de développement sans avoir à les redécouvrir ou bien pour communiquer plus facilement entre l’architecte et le développeur.
Il n’est pas question de les utiliser à chaque ligne de code mais d’être au fait de leur existence pour nous rendre la vie (de programmeur/se…) plus facile !
Sources :