Accueil / Tutoriels / Adapter

Adapter

Publié le 17 Mars 2025
Conception

Adapter (adaptateur en français) est un patron de conception structurel (design pattern) utilisé pour permettre à deux interfaces incompatibles de fonctionner ensemble. L'Adapter va servir de convertisseur entre un code client et le code de notre application.

Principe

L’Adapter encapsule un code client et masque la complexité de la conversion. Le code client encapsulé n’a pas conscience de ce que fait l’Adapter. Le code de notre application n’a pas besoin d’être modifié et continue de fonctionner comme à son habitude.

Exemples d’utilisations

Garder une interopérabilité avec du code legacy

Souvent dans un projet, on doit continuer de faire fonctionner un ancien module qui n’a pas la même interface que le nouveau code. Plutôt que de modifier le code legacy, on créer un Adapter qui transforme les méthodes du code legacy en une interface compatible.

Utilisation d’une API tierce

En utilisant une API tierce, il arrive très souvent que son interface ne concorde pas tout à fait (voire pas du tout) à ce que peut attendre notre application. Une fois de plus l’Adapter fera l’intermédiaire entre l’API et le code de notre application.

Points d’attentions

Le pattern Adapter est très utile, mais présente aussi des limitations et des inconvénients à bien prendre en compte.

Un Adapter ajoute une couche supplémentaire entre le code existant et son utilisation, ce qui alourdit forcément le code en place. Si on multiplie l’utilisation d’Adapter dans notre code code, cela peut aussi être révélateur d’une mauvaise conception du code déjà en place.

Un Adapter est souvent conçu pour standardiser l’interface d’une classe. En faisant cela, on masque d’autres fonctionnalités du code client.

Implémenter un Adapter

On part d’un service existant (une API, une librairie ou autre) qui a déjà une implémentation qui lui est propre, mais qui ne correspond pas à la manière de fonctionner de notre application.

On définit une interface cible. Il s’agit de l’interface attendue par notre application et avec laquelle elle travaille.

On met en place notre Adapter qui contient une instance d’une classe de notre application et convertit les appels.

Exemple d'utilisation d’Adapter en Go

Notre application utilise une API externe pour l’envoi d’email. Le prroblème est que l’API attend un format particulier pour l’email comme un objet structuré, tandis que notre application utilise des chaînes de caractères pour le destinataire, l’objet et le contenu du message.

On définit l'interface que l'application attend pour l'envoi des emails. EmailAdapter est un adaptateur qui transforme l'interface EmailSender en un appel compatible avec ExternalEmailService.


package emailAdapter

import (
   "designpatterns/adapter/emailAPI"
)

type EmailSender interface {
   Send(to string, subject string, body string) error
}

type EmailAdapter struct {
   EmailService *emailAPI.ExternalEmailService
}

func (a *EmailAdapter) Send(to string, subject string, body string) error {
   emailDetails := &emailAPI.EmailDetails{
      Recipient: to,
      Subject:   subject,
      Body:      body,
      IsHTML:    false, // par défaut, on envoie un email en texte brut
   }

   return a.EmailService.SendComplexEmail(emailDetails)
}

ExternalEmailService est un service d'envoi d'email externe avec une méthode complexe et SendComplexEmail est la méthode de l'API externe qui envoie un email.


package emailAPI

import "fmt"
 
type ExternalEmailService struct{}

type EmailDetails struct {
   Recipient  string
   Subject    string
   Body       string
   IsHTML     bool
   Attachment string
}

func (e *ExternalEmailService) SendComplexEmail(details *EmailDetails) error {
   // On simule l'envoi d'un email complexe via l'API externe
   fmt.Printf("Envoi de l'email à %s\nSujet: %s\nContenu: %s\nHTML: %v\nPièce jointe: %s\n",
      details.Recipient, details.Subject, details.Body, details.IsHTML, details.Attachment)
   return nil
}


package main

import (
   "fmt"
   "designpatterns/adapter/emailAPI"
   "designpatterns/adapter/emailAdapter"
)

func main() {
   externalService := &emailAPI.ExternalEmailService{}

   var emailSender emailAdapter.EmailSender = &emailAdapter.EmailAdapter{EmailService: externalService}

   err := emailSender.Send("test@mail.com", "Bienvenue !", "Merci de vous être inscrit à notre service.")
   if err != nil {
      fmt.Println("Erreur lors de l'envoi de l'email:", err)
   }
}

Le même exemple en PHP


namespace DesignPatterns\Adapter;

interface EmailSender
{
   public function send(string $to, string $subject, string $body): bool;
}


namespace DesignPatterns\Adapter;

class EmailAdapter implements EmailSender
{
   private ExternalEmailService $emailService;

   public function __construct(ExternalEmailService $emailService)
   {
       $this->emailService = $emailService;
   }

   public function send(string $to, string $subject, string $body): bool
   {
       $emailDetails = [
           'recipient'  => $to,
           'subject'    => $subject,
           'body'       => $body,
           'isHTML'     => false,
           'attachment' => null
       ];

       return $this->emailService->sendComplexEmail($emailDetails);
   }
}


namespace DesignPatterns\Adapter;

class ExternalEmailService
{
   public function sendComplexEmail(array $emailDetails): bool
   {
       echo "Envoi de l'email à: " . $emailDetails['recipient'] . PHP_EOL;
       echo "Sujet: " . $emailDetails['subject'] . PHP_EOL;
       echo "Contenu: " . $emailDetails['body'] . PHP_EOL;
       echo "HTML: " . ($emailDetails['isHTML'] ? "Oui" : "Non") . PHP_EOL;
       echo "Pièce jointe: " . ($emailDetails['attachment'] ? $emailDetails['attachment'] : "Aucune") . PHP_EOL;

       return true;
   }
}


declare(strict_types=1);

use DesignPatterns\Adapter\EmailAdapter;
use DesignPatterns\Adapter\ExternalEmailService;

require "./vendor/autoload.php";

$externalEmailService = new ExternalEmailService();
$emailSender          = new EmailAdapter($externalEmailService);

$emailSender->send('test@mail.com', 'Bienvenue !', 'Merci de vous être inscrit à notre service.');

Conclusion

J'espère que grâce à cet article vous êtes désormais plus à l'aise avec le design pattern Adapter. Vous pouvez retrouver mon article sur les design patterns ou les autres tutoriels sur Observer, Decorator, Iterator, Chain of responsibility et Factory.