Adapter — ce este, modelul de transformare a interfețelor pentru dezvoltarea mobilă

Autor: IT Sectr Publicat: 2026-02-18 Timp de citire: 10 min

Adapter — este un model structural de proiectare care transformă interfața unei clase în altă interfață așteptată de client. În dezvoltarea mobilă, Adapter este cel mai des folosit în RecyclerView.Adapter pentru Android și UITableViewDataSource pentru iOS. Conform datelor Google I/O (2024), RecyclerView este folosit în 95% din aplicațiile Android pentru afișarea listelor, iar fiecare necesită propria implementare Adapter.

Principalele puncte

  • Adapter — model structural care permite obiectelor cu interfețe incompatibile să lucreze împreună
  • RecyclerView.Adapter — implementarea modelului în Android, care transformă datele în ViewHolder pentru listă
  • UITableViewDataSource — implementarea în iOS, care combină modelele Adapter și DataSource
  • Object Adapter folosește compoziția: adapterul conține o referință către obiectul adaptat
  • Class Adapter folosește moștenirea multiplă — posibilă doar în C++, indisponibilă în Java/Swift/Kotlin

Ce este modelul Adapter?

Adapter — model structural care rezolvă problema incompatibilității interfețelor. Înfășoară un obiect (Adaptee) într-o clasă (Adapter) cu interfața așteptată de client (Target). Clientul lucrează cu Target, fără să știe de existența Adaptee. Aceasta este o variație a modelului Wrapper.

Kotlin
// Clasă existentă cu interfață incompatibilă
class LegacyAuthApi {
    fun loginWithToken(token: String): Map {
        return mapOf("status" to "ok", "user_id" to 42)
    }
}

// Interfață țintă (ce așteaptă clientul)
interface AuthService {
    suspend fun login(credentials: Credentials): Result
}

// Adapter — transformă LegacyAuthApi în 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 transformă apelul login(credentials) în loginWithToken(token) pentru API-ul vechi. Clientul (ViewModel) lucrează prin interfața AuthService și nu știe că sub ea se află cel mai nou Firebase Auth sau un API vechi de zece ani. Acest lucru permite înlocuirea implementărilor fără a modifica codul clientului.

Adapter în Android: RecyclerView și ListAdapter

RecyclerView.Adapter — cea mai răspândită implementare a modelului Adapter în Android. Transformă datele (o listă de obiecte) în ViewHolder-uri pe care RecyclerView le afișează pe ecran. Odată cu apariția ListAdapter (Android Architecture Components), modelul a primit suport încorporat pentru calculul diferențelor pentru animația modificărilor.

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 folosește ListAdapter cu DiffUtil pentru redarea eficientă a listei. Când datele sunt actualizate, DiffCallback calculează diferența dintre lista veche și cea nouă, iar RecyclerView animează doar elementele modificate — fără a redesena întreaga listă. Acesta este avantajul cheie față de ListView-ul mai vechi, care necesita notifyDataSetChanged() manual.

Adapter în iOS: UITableView și DiffableDataSource

UITableViewDataSource — este simultan atât modelul Adapter, cât și DataSource. Transformă datele modelului în celule de tabel. Din iOS 13, Apple a introdus DiffableDataSource — un înlocuitor modern care, asemenea ListAdapter în Android, calculează automat modificările și animează actualizările.

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
    }
}

// Versiune modernă: 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 cu DiffableDataSource animează automat modificările la actualizarea datelor: elementele apar, dispar sau se deplasează cu animații line. DiffableDataSource din iOS și ListAdapter din Android rezolvă aceeași sarcină cu API-uri diferite, dar cu aceeași logică — calculul diferențelor + animație automată.

Object Adapter vs Class Adapter

Object Adapter folosește compoziția: adapterul conține o referință către obiectul Adaptee și îi delegă apelurile. Class Adapter folosește moștenirea: adapterul moștenește Adaptee și implementează simultan interfața țintă. În Java, Swift și Kotlin, Class Adapter este imposibil din cauza lipsei moștenirii multiple — rămâne doar Object Adapter.

CaracteristicăObject AdapterClass Adapter
MecanismCompoziție (conține Adaptee)Moștenire (extends Adaptee)
FlexibilitateMai mare — Adaptee poate fi schimbat în runtimeMai mică — Adaptee este fixat la compilare
CuplareSlabă (prin interfață)Puternică (prin moștenire)
Adaptarea subclaselorDa — Adaptee poate fi orice subclasăNu — doar superclasa concretă
DisponibilitateJava, Kotlin, Swift, C++, C#Doar limbaje cu moștenire multiplă (C++)

În dezvoltarea mobilă se folosește doar Object Adapter. RecyclerView.Adapter conține referințe către date și LayoutInflater, UITableViewDataSource conține un array de elemente. Compoziția face codul mai flexibil și mai testabil comparativ cu moștenirea.

Exemple de Adapter în proiecte reale

Adapter se aplică nu doar pentru liste. Să examinăm trei scenarii din practica dezvoltării mobile, unde modelul rezolvă problema integrării componentelor incompatibile.

ScenariuAdapteeTargetAdapter
Integrarea unei biblioteci vechi de autentificareLegacyAuthLib (callback-based)AuthService (suspend)LegacyAuthAdapter
Adaptarea JSON la un model nouGsonParserJsonParser (Kotlinx Serialization)GsonAdapter
Interfață unică pentru servicii pushFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

Fiecare Adapter încapsulează logica de transformare: GsonAdapter traduce un string JSON într-un obiect prin @SerializedName, PushServiceAdapter abstrage înregistrarea token-ului pentru diferite platforme. Clientul (logica de business) lucrează cu o interfață unică și nu depinde de o implementare concretă.

Erori tipice la implementarea Adapter

Erorile în implementarea Adapter duc la încetinirea listelor, scurgeri de memorie și bug-uri de actualizare a datelor. Să analizăm trei probleme frecvente.

Operații grele în onBindViewHolder

onBindViewHolder este apelat pentru fiecare celulă la derulare. Crearea de obiecte, parsarea JSON sau interogările bazei de date în această metodă duc la tremurul listei (frame drops). Soluție: toate calculele grele să fie făcute dinainte, transmitând datele gata pregătite către adapter. Pentru imagini, folosiți biblioteci cu cache — Glide, Coil, Kingfisher.

Ignorarea DiffUtil / DiffableDataSource

Apelarea notifyDataSetChanged() la fiecare actualizare redesenează întreaga listă, provocând pâlpâiri și pierderea focusului pe câmpurile de intrare. Soluție: folosiți ListAdapter cu DiffUtil în Android sau DiffableDataSource în iOS. Calculul diferențelor durează <1 ms pentru liste de până la 1000 de elemente și asigură animații line.

Stocarea obiectelor grele în adapter

Adapterul trăiește cât RecyclerView/UITableView. Stocarea Bitmap sau a array-urilor mari în adapter duce la scurgeri de memorie la schimbarea configurației (rotirea ecranului, schimbarea temei). Soluție: adapterul ar trebui să stocheze doar modele de date ușoare (data class / struct), iar resursele grele să le încarce prin ViewHolder la cerere.

Întrebări frecvente

Care este diferența dintre Adapter și Proxy?

Adapter transformă interfața Adaptee în interfața Target — clientul primește un nou mod de interacțiune. Proxy oferă aceeași interfață ca și obiectul original, dar adaugă control de acces, cache sau inițializare lentă. Proxy nu schimbă interfața, Adapter — schimbă.

De ce RecyclerView.Adapter este un exemplu al modelului Adapter?

RecyclerView.Adapter transformă datele (de exemplu, o listă de User) în ViewHolder pe care RecyclerView îl poate afișa. RecyclerView așteaptă ViewHolder, datele sunt în format List<User> — Adapter le adaptează pe unul la celălalt. În plus, Adapter gestionează ciclul de viață al ViewHolder-urilor prin pool-ul de reciclare, îmbunătățind performanța listei.

Care este diferența dintre Object Adapter și Class Adapter?

Object Adapter folosește compoziția: conține o referință către Adaptee și îi delegă apelurile. Class Adapter folosește moștenirea multiplă: moștenește Adaptee și implementează Target. În Java, Kotlin și Swift, doar Object Adapter este disponibil din cauza lipsei moștenirii multiple de clase. Class Adapter este posibil doar în C++.

Când să folosim Adapter în loc de Facade?

Adapter este folosit când trebuie să facem două clase existente să funcționeze împreună, transformând interfața uneia dintre ele. Facade — când trebuie să simplificăm interacțiunea cu un subsistem complex, oferind o interfață simplă. Adapter — pentru compatibilitate, Facade — pentru simplificare.

Se poate implementa Adapter fără interfață?

Da, prin închideri (closures). În locul unei interfețe cu o singură metodă, se poate transmite o funcție. De exemplu, în Swift: let adapter = { (data: Data) -> [CellConfig] in /* transformare */ }. Această abordare se numește functional adapter. Însă pentru 2+ metode, protocolul/interfața rămâne preferată — face contractul explicit.

Concluzii

  • Adapter — model structural care transformă interfața incompatibilă Adaptee în Target printr-o clasă intermediară
  • RecyclerView.Adapter (Android) și UITableViewDataSource (iOS) — principalele exemple ale modelului în dezvoltarea mobilă
  • Object Adapter folosește compoziția; Class Adapter (doar C++) — moștenirea multiplă
  • ListAdapter + DiffUtil (Android) și DiffableDataSource (iOS) — evoluții moderne cu calcul automat al diferențelor și animație
  • Operații grele în onBindViewHolder, ignorarea DiffUtil și stocarea resurselor în adapter — erori frecvente
  • Adapter se aplică pentru integrarea codului vechi, adaptarea formatelor și unificarea diferitelor implementări
  • Recomandare: folosiți ListAdapter/DiffableDataSource în locul gestionării manuale a actualizărilor listei

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și