Adapter — qu'est-ce que c'est, le pattern de transformation d'interfaces pour le développement mobile

Auteur : IT Sectr Publié le : 2026-02-18 Temps de lecture : 10 min

Adapter est un patron de conception structurel qui convertit l'interface d'une classe en une autre interface attendue par le client. Dans le développement mobile, Adapter est le plus souvent utilisé dans RecyclerView.Adapter pour Android et UITableViewDataSource pour iOS. Selon Google I/O (2024), RecyclerView est utilisé dans 95% des applications Android pour afficher des listes, et chacune nécessite sa propre implémentation d'Adapter.

Points clés

  • Adapter est un patron structurel qui permet à des objets avec des interfaces incompatibles de fonctionner ensemble
  • RecyclerView.Adapter est une implémentation du pattern dans Android, convertissant des données en ViewHolders pour la liste
  • UITableViewDataSource est une implémentation dans iOS, combinant les patterns Adapter et DataSource
  • Object Adapter utilise la composition : l'adaptateur contient une référence à l'objet adapté
  • Class Adapter utilise l'héritage multiple — possible uniquement en C++, pas disponible en Java/Swift/Kotlin

Qu'est-ce que le pattern Adapter ?

Adapter est un patron structurel qui résout le problème d'incompatibilité des interfaces. Il encapsule un objet (Adaptee) dans une classe (Adapter) avec l'interface attendue par le client (Target). Le client travaille avec Target sans connaître l'existence d'Adaptee. C'est une variante du pattern Wrapper.

Kotlin
// Classe existante avec interface incompatible
class LegacyAuthApi {
    fun loginWithToken(token: String): Map {
        return mapOf("status" to "ok", "user_id" to 42)
    }
}

// Interface cible (ce que le client attend)
interface AuthService {
    suspend fun login(credentials: Credentials): Result
}

// Adapter — transforme LegacyAuthApi en AuthService
class LegacyAuthAdapter(
    private val legacyApi: LegacyAuthApi
) : AuthService {
    override suspend fun login(credentials: Credentials): Result {
        val token = "${credentials.login}:${credentials.password}"
            .encodeToByteArray().let { Base64.encodeToString(it) }
        val response = legacyApi.loginWithToken(token)
        return if (response["status"] == "ok") {
            Result.success(User(response["user_id"] as Int))
        } else {
            Result.failure(AuthException("Login failed"))
        }
    }
}

LegacyAuthAdapter convertit l'appel login(credentials) en loginWithToken(token) pour l'ancienne API. Le client (ViewModel) travaille via l'interface AuthService et ne sait pas si en dessous se trouve le tout dernier Firebase Auth ou une API legacy vieille d'une décennie. Cela permet de remplacer les implémentations sans modifier le code client.

Adapter dans Android : RecyclerView et ListAdapter

RecyclerView.Adapter est l'implémentation la plus courante du pattern Adapter dans Android. Il convertit les données (une liste d'objets) en ViewHolders que RecyclerView affiche à l'écran. Avec l'introduction de ListAdapter (Android Architecture Components), le pattern a obtenu un support intégré de calcul de différences pour animer les changements.

Kotlin
class UserAdapter(
    private val onItemClick: (User) -> Unit
) : ListAdapter(DiffCallback()) {

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): UserViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_user, parent, false)
        return UserViewHolder(view)
    }

    override fun onBindViewHolder(
        holder: UserViewHolder,
        position: Int
    ) {
        holder.bind(getItem(position), onItemClick)
    }

    class UserViewHolder(itemView: View) :
        RecyclerView.ViewHolder(itemView) {
        
        private val tvName = itemView.findViewById(R.id.tvName)
        
        fun bind(user: User, click: (User) -> Unit) {
            tvName.text = user.name
            itemView.setOnClickListener { click(user) }
        }
    }

    private class DiffCallback : DiffUtil.ItemCallback() {
        override fun areItemsTheSame(old: User, new: User) = old.id == new.id
        override fun areContentsTheSame(old: User, new: User) = old == new
    }
}

UserAdapter utilise ListAdapter avec DiffUtil pour un redessin efficace de la liste. Lorsque les données sont mises à jour, DiffCallback calcule la différence entre l'ancienne et la nouvelle liste, et RecyclerView n'anime que les éléments modifiés — sans redessiner la liste entière. C'est un avantage clé par rapport à l'ancien ListView qui nécessitait un notifyDataSetChanged() manuel.

Adapter dans iOS : UITableView et DiffableDataSource

UITableViewDataSource est à la fois le pattern Adapter et le pattern DataSource. Il convertit les données du modèle en cellules de tableau. Depuis iOS 13, Apple a introduit DiffableDataSource — un remplacement moderne qui, comme ListAdapter dans Android, calcule automatiquement les changements et anime les mises à jour.

Swift
class UserTableViewAdapter: NSObject, UITableViewDataSource {
    var items: [User] = []
    
    func tableView(_ tableView: UITableView,
                    numberOfRowsInSection section: Int) -> Int {
        items.count
    }
    
    func tableView(_ tableView: UITableView,
                    cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(
            withIdentifier: "UserCell", for: indexPath)
        let user = items[indexPath.row]
        cell.textLabel?.text = user.name
        return cell
    }
}

// Version moderne : DiffableDataSource
enum Section { case main }

class ModernUserAdapter {
    var dataSource: UITableViewDiffableDataSource<Section, User>!
    
    func configure(for tableView: UITableView) {
        dataSource = UITableViewDiffableDataSource(
            tableView: tableView
        ) { tableView, indexPath, user in
            let cell = tableView.dequeueReusableCell(
                withIdentifier: "UserCell", for: indexPath)
            cell.textLabel?.text = user.name
            return cell
        }
    }
    
    func update(with users: [User]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, User>()
        snapshot.appendSections([.main])
        snapshot.appendItems(users)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

ModernUserAdapter avec DiffableDataSource anime automatiquement les changements lors de la mise à jour des données : les éléments apparaissent, disparaissent ou se déplacent avec des animations fluides. DiffableDataSource d'iOS et ListAdapter d'Android résolvent le même problème avec des API différentes mais la même logique — calcul de différences + animation automatique.

Object Adapter vs Class Adapter

Object Adapter utilise la composition : l'adaptateur contient une référence à l'objet Adaptee et lui délègue les appels. Class Adapter utilise l'héritage : l'adaptateur hérite d'Adaptee et implémente simultanément l'interface cible. En Java, Swift et Kotlin, Class Adapter est impossible en raison de l'absence d'héritage multiple — seul Object Adapter reste possible.

CaractéristiqueObject AdapterClass Adapter
MécanismeComposition (contient Adaptee)Héritage (étend Adaptee)
FlexibilitéPlus élevée — Adaptee peut être changé à l'exécutionPlus faible — Adaptee est fixé à la compilation
CouplageFaible (via interface)Fort (via héritage)
Adaptation des sous-classesOui — Adaptee peut être n'importe quelle sous-classeNon — seulement une superclasse spécifique
DisponibilitéJava, Kotlin, Swift, C++, C#Langues avec héritage multiple uniquement (C++)

Dans le développement mobile, seul Object Adapter est utilisé. RecyclerView.Adapter contient des références aux données et à LayoutInflater, UITableViewDataSource contient un tableau d'éléments. La composition rend le code plus flexible et testable par rapport à l'héritage.

Exemples d'Adapter dans des projets réels

Adapter n'est pas utilisé seulement pour les listes. Considérons trois scénarios de la pratique du développement mobile où le pattern résout le problème d'intégration de composants incompatibles.

ScénarioAdapteeTargetAdapter
Intégration d'une bibliothèque auth legacyLegacyAuthLib (basée sur callbacks)AuthService (suspend)LegacyAuthAdapter
Adaptation JSON à un nouveau modèleGsonParserJsonParser (Kotlinx Serialization)GsonAdapter
Interface unifiée pour les services pushFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

Chaque Adapter encapsule la logique de conversion : GsonAdapter traduit une chaîne JSON en objet via @SerializedName, PushServiceAdapter abstrait l'enregistrement de token pour différentes plateformes. Le client (logique métier) travaille avec une interface unifiée et ne dépend pas de l'implémentation spécifique.

Erreurs courantes lors de l'implémentation d'Adapter

Les erreurs dans l'implémentation d'Adapter entraînent des ralentissements de listes, des fuites mémoire et des bugs de mise à jour des données. Examinons trois problèmes courants.

Opérations lourdes dans onBindViewHolder

onBindViewHolder est appelé pour chaque cellule lors du défilement. La création d'objets, l'analyse JSON ou l'appel à la base de données dans cette méthode provoquent des chutes d'images (frame drops). Solution : effectuer tous les calculs lourds à l'avance, en passant des données prêtes à l'adaptateur. Utiliser des bibliothèques de cache pour les images — Glide, Coil, Kingfisher.

Ignorer DiffUtil / DiffableDataSource

Appeler notifyDataSetChanged() à chaque mise à jour redessine toute la liste, provoquant un scintillement et une perte de focus sur les entrées. Solution : utiliser ListAdapter avec DiffUtil dans Android ou DiffableDataSource dans iOS. Le calcul de différences prend <1 ms pour des listes jusqu'à 1000 éléments et garantit des animations fluides.

Stocker des objets lourds dans l'adaptateur

L'adaptateur vit aussi longtemps que RecyclerView/UITableView. Stocker des Bitmaps ou de grands tableaux dans l'adaptateur entraîne des fuites mémoire lors des changements de configuration (rotation d'écran, changement de thème). Solution : l'adaptateur ne doit stocker que des modèles de données légers (data class / struct), et charger les ressources lourdes via ViewHolder à la demande.

Foire aux questions

Quelle est la différence entre Adapter et Proxy ?

Adapter convertit l'interface d'Adaptee en l'interface de Target — le client obtient une nouvelle façon d'interagir. Proxy fournit la même interface que l'objet original, mais ajoute un contrôle d'accès, du cache ou une initialisation paresseuse. Proxy ne change pas l'interface, Adapter si.

Pourquoi RecyclerView.Adapter est-il un exemple du pattern Adapter ?

RecyclerView.Adapter convertit les données (par exemple, une liste d'User) en un ViewHolder que RecyclerView peut afficher. RecyclerView attend un ViewHolder, les données sont au format List<User> — l'Adapter adapte l'un à l'autre. De plus, l'Adapter gère le cycle de vie des ViewHolders via le pool de recyclage, améliorant les performances de la liste.

Quelle est la différence entre Object Adapter et Class Adapter ?

Object Adapter utilise la composition : il contient une référence à Adaptee et lui délègue les appels. Class Adapter utilise l'héritage multiple : il hérite d'Adaptee et implémente Target. En Java, Kotlin et Swift, seul Object Adapter est disponible en raison de l'absence d'héritage multiple de classes. Class Adapter n'est possible qu'en C++.

Quand utiliser Adapter au lieu de Facade ?

Adapter est utilisé lorsque vous devez faire fonctionner deux classes existantes ensemble en convertissant l'interface de l'une d'elles. Facade est utilisé lorsque vous devez simplifier l'interaction avec un sous-système complexe en fournissant une interface simple. Adapter est pour la compatibilité, Facade pour la simplification.

Peut-on implémenter Adapter sans interface ?

Oui, en utilisant des fermetures. Au lieu d'une interface avec une méthode, vous pouvez passer une fonction. Par exemple, en Swift : let adapter = { (data: Data) -> [CellConfig] in /* conversion */ }. Cette approche s'appelle un adaptateur fonctionnel. Cependant, pour 2+ méthodes, un protocole/interface reste préférable — il rend le contrat explicite.

Résumé

  • Adapter est un patron structurel qui convertit une interface Adaptee incompatible en Target via une classe intermédiaire
  • RecyclerView.Adapter (Android) et UITableViewDataSource (iOS) sont les principaux exemples du pattern dans le développement mobile
  • Object Adapter utilise la composition ; Class Adapter (C++ uniquement) utilise l'héritage multiple
  • ListAdapter + DiffUtil (Android) et DiffableDataSource (iOS) sont des évolutions modernes avec diff automatique et animation
  • Opérations lourdes dans onBindViewHolder, ignorance de DiffUtil et stockage de ressources dans l'adaptateur sont des erreurs courantes
  • Adapter est utilisé pour intégrer du code legacy, adapter des formats et unifier différentes implémentations
  • Recommandation : utilisez ListAdapter/DiffableDataSource au lieu de la gestion manuelle des mises à jour de liste

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi