image de chargement
Retour au glossaire

Adapter (Pattern Adaptateur)

Pattern structurel permettant la collaboration entre interfaces incompatibles en convertissant l'interface d'une classe en une autre attendue par le client.

Mis à jour le 9 janvier 2026

Le pattern Adapter, aussi appelé Wrapper, est un design pattern structurel qui permet à des interfaces incompatibles de collaborer. Il agit comme un traducteur entre deux systèmes qui ne peuvent pas communiquer directement, encapsulant la logique de conversion pour rendre transparente l'interaction entre composants hétérogènes.

Fondements

  • Convertit l'interface d'une classe existante (Adaptee) en une interface attendue par le client (Target)
  • Permet la réutilisation de code legacy ou de bibliothèques tierces sans modification de leur code source
  • Découple le client de l'implémentation concrète en introduisant une couche d'abstraction
  • Respecte le principe Open/Closed en permettant l'extension sans modification du code existant

Avantages

  • Isolation des responsabilités : sépare la logique métier de la conversion d'interfaces
  • Réutilisabilité accrue : intègre facilement du code tiers ou legacy dans de nouvelles architectures
  • Flexibilité : permet de changer l'implémentation adaptée sans impacter le client
  • Testabilité améliorée : facilite le mocking et les tests unitaires grâce à l'abstraction
  • Maintenabilité : centralise la logique de conversion en un point unique

Exemple concret

Imaginons une application nécessitant un service de notification, mais devant intégrer différents fournisseurs (email SMTP, API tierce, webhook). Chaque fournisseur possède sa propre interface incompatible avec notre système.

notification-adapter.ts
// Interface cible attendue par notre application
interface NotificationService {
  send(recipient: string, message: string): Promise<void>;
}

// Service legacy avec interface incompatible
class LegacySMTPService {
  sendEmail(to: string, subject: string, body: string): void {
    console.log(`SMTP: Sending to ${to}`);
    // Logique SMTP complexe...
  }
}

// API tierce avec interface différente
class ThirdPartyAPI {
  async postNotification(payload: { user: string; text: string }): Promise<{ status: number }> {
    console.log(`API: Posting notification`);
    return { status: 200 };
  }
}

// Adapter pour le service SMTP
class SMTPAdapter implements NotificationService {
  constructor(private smtpService: LegacySMTPService) {}

  async send(recipient: string, message: string): Promise<void> {
    // Conversion de l'interface
    this.smtpService.sendEmail(recipient, 'Notification', message);
  }
}

// Adapter pour l'API tierce
class ThirdPartyAdapter implements NotificationService {
  constructor(private api: ThirdPartyAPI) {}

  async send(recipient: string, message: string): Promise<void> {
    // Adaptation du format
    const result = await this.api.postNotification({
      user: recipient,
      text: message
    });
    if (result.status !== 200) {
      throw new Error('Notification failed');
    }
  }
}

// Utilisation côté client
class NotificationManager {
  constructor(private notifier: NotificationService) {}

  async notifyUser(userId: string, msg: string): Promise<void> {
    await this.notifier.send(userId, msg);
  }
}

// Configuration flexible
const smtpNotifier = new NotificationManager(
  new SMTPAdapter(new LegacySMTPService())
);

const apiNotifier = new NotificationManager(
  new ThirdPartyAdapter(new ThirdPartyAPI())
);

await smtpNotifier.notifyUser('user@example.com', 'Hello!');
await apiNotifier.notifyUser('user123', 'Hello!');

Mise en œuvre

  1. Identifier l'interface cible (Target) que le client attend et utilise
  2. Analyser l'interface incompatible (Adaptee) du composant à intégrer
  3. Créer une classe Adapter implémentant l'interface Target
  4. Injecter une instance de l'Adaptee dans le constructeur de l'Adapter
  5. Implémenter les méthodes Target en déléguant et transformant les appels vers l'Adaptee
  6. Injecter l'Adapter au client via dependency injection pour maintenir le découplage
  7. Tester l'intégration avec des tests unitaires et d'intégration

Conseil Pro

Privilégiez la composition (injection de dépendance) plutôt que l'héritage pour implémenter le pattern Adapter. Cela offre plus de flexibilité et respecte mieux le principe de composition over inheritance. En TypeScript/JavaScript, utilisez des interfaces explicites pour documenter les contrats, et considérez l'utilisation de factory functions pour simplifier l'instanciation d'adapters complexes avec configuration.

Outils associés

  • TypeScript : typage fort pour définir clairement les interfaces Target et Adaptee
  • Inversify / TSyringe : conteneurs IoC facilitant l'injection d'adapters
  • Jest / Vitest : frameworks de test pour valider le comportement des adapters
  • class-transformer : automatisation de la transformation d'objets entre formats
  • Swagger / OpenAPI : génération automatique d'adapters pour APIs REST

Le pattern Adapter représente un investissement stratégique pour la pérennité architecturale. En isolant les dépendances externes et en standardisant les interfaces, il réduit significativement les coûts de maintenance et facilite l'évolution technologique. Cette approche permet aux équipes de migrer progressivement vers de nouvelles solutions tout en maintenant la stabilité opérationnelle, générant une valeur métier durable à travers une architecture résiliente et évolutive.

L'argentestdéjàsurlatable.

En 1 heure, découvrez exactement combien vous perdez et comment le récupérer.