Il suffit de s’être déjà attardé dessus pour se rendre compte que la conception d’un projet informatique est aussi importante, si ce n’est plus, que le code employé dans sa réalisation. Une bonne conception peut vous éviter de nombreuses heures perdues, une perte de cheveux inutile (il y a déjà bien assez d’occasions!) et surtout apporter une facilité de transmission et d’échanges si d’autres personnes étaient amenées à travailler avec vous. C’est ici qu’intervient le structural adapter.
Structural adapter, que l’on pourrait traduire en français par un adaptateur structurel, est un design pattern (patron de conception, i.e. un arrangement caractéristique de modules) permettant d’implémenter l’utilisation d’une interface (ou module, classe,…) de telle manière qu’elle soit utilisable dans un contexte précis, sans modifier celle-ci. En d’autres termes, cet outil permet de rendre compatibles deux interfaces qui ne le sont pas à l’origine.
Et concrètement, en quoi ça consiste?
A titre d’exemple, dans un projet où vous avez besoin d’utiliser une bibliothèque précise mais que vous ne souhaitez pas modifier les méthodes de cette bibliothèque, il vous suffira de créer une nouvelle classe réutilisant ses méthodes pour les adapter à votre client, qui utilisera par la suite les méthodes de la classe que vous venez de créer. C’est pour cela que le terme d’adaptateur est très juste : en partant d’une interface initiale, vous allez instancier un objet utilisant les méthodes de celle-ci de telle sorte que les méthodes implémentées correspondent au contexte voulu par votre client consommateur. Celui-ci peut être complètement différent ou au contraire simplement “améliorer” le contexte précédent. Le dernier cas échéant on pourrait faire un parallèle avec l’héritage en programmation orientée objet où l’on dispose :
- d’une classe mère (par exemple “chat”)
- d’une classe fille (“chat blanc”)
La classe fille spécifie donc un attribut de la classe mère et les méthodes de la classe fille vont redéfinir celles qu’elle aura héritées pour être applicables à son cas bien précis. Leurs comportements pourront être modifiés, ou non. L’adaptateur apporte donc les avantages de l’héritage en POO sans avoir besoin de liens (directs) entre les classes!
Mais puisque des lignes de code parlent souvent plus que de longues phrases, voici une application possible dans un autre exemple (en pseudo-Code JAVA) :
On dispose tout d’abord de deux interfaces différentes et on les implémente :
interface Rigoureux
{
public void travailler();
}interface Faineant
{
public void jouer();
}class EtudiantSerieux implements Rigoureux
{
public void travailler() {
System.out.println("Je révise mes cours d'ELP...");
}
}class EtudiantPasSerieux implements Faineant
{
public void jouer() {
System.out.println("Je préfère jouer !");
}
}
Pour l’instant un EtudiantPasSerieux ne peut pas travailler car l’interface qu’il implémente ne lui offre pas cette possibilité, ce qui est embêtant pour avoir de bons résultats… On va donc créer un adaptateur pour lui permettre de lui aussi réussir !
class EtudiantAdapter implements Faineant
{
EtudiantSerieux etudiant; public EtudiantAdapter(EtudiantSerieux etudiant){
this.etudiant = etudiant;
} public void jouer() {
etudiant.travailler();
}
}
Et on utilise cet adaptateur de la manière suivante :
class Main
{
public static void main(String args[])
{
EtudiantSerieux e1 = new EtudiantSerieux();
EtudiantPasSerieux e2 = new EtudiantPasSerieux();
EtudiantPasSerieux e3 = new EtudiantAdapter(e1); e1.travailler(); //Affiche "Je révise mes cours d'ELP..."
e2.jouer(); //Affiche "Je préfère jouer !"
e3.jouer(); //Affiche "Je révise mes cours d'ELP..."
}
}
L’adaptateur que l’on a créé nous permet donc d’utiliser les méthodes de l’interface Rigoureux avec un objet de type EtudiantPasSerieux. Et voilà, même l’élève fainéant travaille finalement !
Ce type de structure peut s’avérer très utile lorsque vous disposez de codes sources extérieurs dont vous ne connaissez que les attributs et le résultat, et que vous souhaitez les utiliser d’une manière légèrement différente. Un adaptateur peut être un moyen simple de les utiliser selon vos attentes sans avoir besoin de les comprendre dans leur totalité.
Super, et ça peut servir autrement?
Tout à fait! Un adaptateur peut également être utilisé dans le cas où l’on ne souhaite pas développer entièrement toutes les fonctionnalités d’une interface. Il fournit alors un comportement par défaut pour les méthodes qui ne sont pas redéfinies (voir ci-après un exemple Java MouseListener / MouseAdapter).
public class MouseAction implements MouseListener
{
public void mouseClicked(MouseEvent e) {
//ACTION
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
Dans le cas de MouseListener il est nécessaire de redéfinir toutes les méthodes du module (même si il n’y a finalement aucun intérêt).
public class MouseAction extends MouseAdapter
{
public void mouseClicked(MouseEvent e) {
//ACTION
}
}
Une alternative beaucoup plus légère au code précédent avec l’utilisation de MouseAdapter. Ici on ne redéfinie que les méthodes qui nous intéressent. Plutôt pratique, non?
Des conditions particulières d’utilisation ?
En théorie non mais en pratique c’est plus compliqué. De nos jours, la plupart des langages de haut niveau implémentent des modules et bibliothèques spécifiquement dédiées à l’utilisation d’adaptateurs tandis que les langages de bas niveau n’en ont pas ou peu de base. Dans cet article je n’ai fait des démonstrations qu’avec des pseudos-codes basés sur le langage JAVA et les structures qui y sont manipulées s’inscrivent dans une Programmation Orientée Objet (POO). Ce type de programmation s’inclut parfaitement dans l’utilisation d’adaptateurs et ils y sont très souvent utilisés. Néanmoins cet outil ne se limite pas qu’à la POO et même si l’utilisation est moins immédiate dans d’autres types de langages (comme le C par exemple), elle est tout à fait possible en recréant soi-même des structures basées sur le même principe. Donc aucune inquiétude, vous pourrez appliquer cette stratégie dans de très nombreux cas !
Pour conclure je dirais que même si un adaptateur ne pourra pas coder pour vous, il simplifiera sans aucun doute votre au code et vous évitera de nombreux problèmes.
Sources :
- Wikipédia : https://en.wikipedia.org/wiki/Adapter_pattern
- GeekForGeeks : https://www.geeksforgeeks.org/adapter-pattern/
- DZone : https://dzone.com/articles/structural-design-patterns-adapter-pattern
- Quora : https://www.quora.com/What-are-some-design-patterns-to-use-in-non-OOP-languages-to-create-big-programs
- SoftwareEngineering : https://softwareengineering.stackexchange.com/questions/67594/non-oop-design-patterns