Vous avez déjà dû intégrer une bibliothèque ou un module legacy dont l’interface ne correspond pas à votre application ? Le patron adaptateur traduit une interface en une autre pour réutiliser du code existant sans le modifier. Ce guide présente le patron avec des exemples concrets en TypeScript, React et Node.js, et montre comment moderniser des modules basés sur des callbacks.
December 19, 2025 (4mo ago) — last updated April 13, 2026 (17d ago)
Patron Adaptateur — TypeScript, React et Node.js
Un guide pratique pour unifier des interfaces incompatibles avec des exemples en TypeScript, React et Node.js afin de moderniser des intégrations et du code legacy.
← Back to blog
Patron Adaptateur — TypeScript, React et Node.js
Résumé : Découvrez comment le patron adaptateur unifie des interfaces incompatibles avec des exemples concrets en TypeScript, React et Node.js pour moderniser des intégrations et du code legacy.
Introduction
Vous avez déjà dû intégrer une bibliothèque ou un module legacy qui fonctionne bien mais dont l’interface ne correspond pas à votre application ? Le patron adaptateur résout ce problème en traduisant une interface en une autre, pour réutiliser du code existant sans le modifier. Ce guide explique le patron, propose des exemples pratiques en TypeScript et React, et montre comment adapter un module Node.js basé sur des callbacks pour le rendre compatible avec async/await.
Pourquoi le patron adaptateur est important
Le patron adaptateur est un patron structurel qui encapsule un objet incompatible et expose l’interface attendue par votre code. Il a été documenté par le Gang of Four en 19941. Les adaptateurs sont essentiels pour intégrer des API tierces, moderniser du code legacy et unifier des sources de données disparates.
Scénarios courants où les adaptateurs sont utiles :
- Intégrer des API tierces avec des formats de données différents.
- Moderniser des API legacy basées sur des callbacks pour les utiliser avec async/await.
- Unifier plusieurs sources de données en une seule interface pour les composants UI.
L’utilisation d’un adaptateur permet de garder la logique métier propre et découplée des systèmes externes.
Le patron adaptateur en un coup d’œil
| Concept | Description |
|---|---|
| Type | Structurel |
| Intent principal | Permettre à des objets aux interfaces incompatibles de fonctionner ensemble |
| Idée centrale | Encapsuler l’adaptee pour exposer l’interface cible |
| Problème résolu | Réutiliser des classes existantes sans modifier leur code source |
| Cas d’usage | Bibliothèques tierces, code legacy, sources de données multiples |
Structure et rôles
Le patron comporte quatre rôles :
- Le Client — le code qui a besoin d’une interface spécifique.
- L’Interface Cible — le contrat attendu par le Client.
- L’Adaptee — la classe ou le module incompatible qui possède la fonctionnalité nécessaire.
- L’Adaptateur — implémente la Cible et délègue à l’Adaptee, traduisant les appels si besoin.
Deux styles courants :
- Adaptateur objet (composition) : l’adaptateur contient une instance de l’Adaptee ; approche flexible.
- Adaptateur de classe (héritage) : l’adaptateur hérite de l’Adaptee et implémente la Cible ; moins courant en JavaScript/TypeScript modernes.
Exemple pratique : TypeScript + React
Imaginez un tableau de bord qui reçoit des profils utilisateur de deux services avec des formes de réponse différentes. Sans adaptateur, les composants s’alourdissent de logique conditionnelle.
Formes d’API incompatibles
// Data from UserServiceA
interface UserA {
userId: number;
fullName: string;
emailAddress: string;
}
// Data from UserServiceB
interface UserB {
id: string;
name: string;
contact: {
email: string;
};
}
Interface cible attendue par l’application
interface UnifiedUser {
id: string;
name: string;
email: string;
}
Adaptateurs TypeScript
// Adapter for UserServiceA
function adaptUserA(userA: UserA): UnifiedUser {
return {
id: userA.userId.toString(),
name: userA.fullName,
email: userA.emailAddress,
};
}
// Adapter for UserServiceB
function adaptUserB(userB: UserB): UnifiedUser {
return {
id: userB.id,
name: userB.name,
email: userB.contact.email,
};
}
Centraliser les transformations permet de garder les composants propres et résilients. Si une API renomme un champ, seul l’adaptateur change.
Composant React consommant des données unifiées
interface UserProfileProps {
user: UnifiedUser;
}
const UserProfile: React.FC<UserProfileProps> = ({ user }) => {
return (
<div>
<h2>{user.name}</h2>
<p>ID: {user.id}</p>
<p>Email: {user.email}</p>
</div>
);
};
Ce composant s’appuie sur une forme unique et prévisible, ce qui facilite les tests et la réutilisation.
Exemple : moderniser un module Node.js basé sur des callbacks
Les modules legacy utilisent souvent des callbacks avec la convention error-first. Plutôt que de modifier un module stable, créez un adaptateur qui expose une API basée sur les Promises.
Adaptee legacy (ne pas modifier)
// legacyFileProcessor.js
const fs = require('fs');
class LegacyFileProcessor {
processFile(filePath, callback) {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
return callback(err, null);
}
const processedContent = data.toUpperCase();
callback(null, processedContent);
});
}
}
module.exports = LegacyFileProcessor;
Adaptateur qui renvoie une Promise
// FileProcessorAdapter.js
const LegacyFileProcessor = require('./legacyFileProcessor');
class FileProcessorAdapter {
constructor() {
this.legacyProcessor = new LegacyFileProcessor();
}
processFile(filePath) {
return new Promise((resolve, reject) => {
this.legacyProcessor.processFile(filePath, (err, data) => {
if (err) return reject(err);
resolve(data);
});
});
}
}
module.exports = FileProcessorAdapter;
Cette approche reflète util.promisify de Node.js mais garde la logique d’adaptation explicite et testable3.
Utiliser l’adaptateur dans le code applicatif
const FileProcessorAdapter = require('./FileProcessorAdapter');
const fileProcessor = new FileProcessorAdapter();
async function handleFileProcessing() {
try {
console.log('Processing file with modern async/await...');
const content = await fileProcessor.processFile('my-file.txt');
console.log('Processed Content:', content);
} catch (error) {
console.error('An error occurred:', error);
}
}
handleFileProcessing();
Cela permet de laisser le code legacy intact tout en fournissant au reste de la base de code une interface moderne.
Quand utiliser un adaptateur
Utilisez un adaptateur lorsque deux composants ne peuvent pas communiquer directement à cause d’une incompatibilité d’interface. Scénarios typiques :
- Intégrer une API tierce dont les entrées ou sorties ne correspondent pas à vos modèles.
- Envelopper des API legacy basées sur des callbacks pour qu’elles fonctionnent avec async/await.
- Prendre en charge plusieurs sources de données avec des formats différents en créant un adaptateur par source.
Quand ne pas utiliser un adaptateur :
- Si vous contrôlez les deux systèmes et qu’un petit refactor les aligne, préférez la refonte directe.
- Si l’objectif est de simplifier un sous-système, envisagez un Facade qui offre un point d’entrée de haut niveau.
Liste de décision rapide
| Situation | Utiliser un adaptateur ? | Pourquoi |
|---|---|---|
| Utiliser une bibliothèque tierce avec une API incompatible | Oui | Vous ne pouvez pas changer la bibliothèque, donc adaptez-vous à elle |
| Contrôlez les deux côtés et le changement est minime | Non | Refactorez directement pour éviter une indirection |
| Besoin d’une interface simplifiée de haut niveau | Non | Le Facade est mieux adapté |
| Migration incrémentale de systèmes legacy | Oui | Enveloppez les anciens composants pour correspondre aux nouvelles interfaces |
| Multiples sources de données structurées différemment | Oui | Les adaptateurs les unifient en une forme unique |
Tests et performance
Les adaptateurs améliorent la testabilité en découplant la logique cœur des systèmes externes. Vous pouvez simuler l’interface d’un adaptateur pour tester les composants en isolation, et tester les adaptateurs séparément pour vérifier la logique de traduction.
La surcharge de performance due à un adaptateur est minimale — généralement un appel de fonction supplémentaire — et négligeable par rapport aux E/S réseau ou aux requêtes de base de données. Pour la plupart des applications web, les bénéfices de maintenance et de découplage l’emportent largement sur ce coût minime. JavaScript reste le langage le plus utilisé selon le rapport Stack Overflow, ce qui illustre la fréquence des travaux d’intégration que les adaptateurs permettent de résoudre4.
Questions fréquentes
Q : Quel problème résout le patron adaptateur ?
R : Il résout les incompatibilités d’interface en traduisant les appels d’un client en appels compréhensibles par l’adaptee, permettant de réutiliser du code sans le modifier.
Q : Comment un adaptateur aide-t-il avec du code legacy ?
R : Un adaptateur enveloppe des modules legacy et expose une interface moderne, permettant d’intégrer du code ancien et stable dans de nouvelles applications sans réécritures risquées.
Q : Quand devrais-je choisir un adaptateur plutôt qu’un autre patron ?
R : Choisissez un adaptateur lorsque vous avez besoin de compatibilité entre deux interfaces incompatibles. Si vous voulez simplifier un système entier, utilisez un Facade.
Q&A rapides
Q1 — Comment commencer à implémenter un adaptateur ? A1 — Identifiez la forme de données attendue par votre application, créez une interface cible et écrivez des fonctions d’adaptation qui convertissent chaque source vers cette interface.
Q2 — Les adaptateurs compliquent-ils le débogage ? A2 — Non, s’ils sont bien testés et isolés. Ils centralisent la logique de traduction et réduisent la duplication, ce qui facilite le débogage.
Q3 — Faut-il un adaptateur par source ? A3 — Oui, créer un adaptateur par source garde la responsabilité claire et facilite la maintenance et les tests.
Lectures complémentaires
- Approfondissement sur polymorphisme vs héritage : /blog/polymorphism-vs-inheritance
- Stratégies pour moderniser les systèmes legacy : /blog/modernizing-legacy-systems
- Référence et exemples du patron adaptateur : https://www.geeksforgeeks.org/adapter-pattern/2
Chez Clean Code Guy, nous aidons les équipes à mettre en œuvre des patrons de conception pratiques qui transforment des bases de code fragiles en actifs résilients, testables et agréables à maintenir. Si vous luttez avec un système legacy ou des intégrations difficiles, nos Clean Code Audits peuvent vous fournir une feuille de route claire et exploitable vers une architecture plus saine. Découvrez comment nous pouvons vous aider à livrer du meilleur code, plus rapidement.
L’IA écrit du code.Vous le faites durer.
À l’ère de l’accélération de l’IA, le code propre n’est pas seulement une bonne pratique — c’est la différence entre les systèmes qui évoluent et les codebases qui s’effondrent sous leur propre poids.