Adapter — mi ez, interfész átalakítási minta mobilfejlesztéshez

Szerző: IT Sectr Megjelenés: 2026-02-18 Olvasási idő: 10 perc

Adapter — egy strukturális tervezési minta, amely egy osztály interfészét egy másik, az ügyfél által várt interfésszé alakítja. Mobilfejlesztésben az Adaptert leggyakrabban az Android rendszerben a RecyclerView.Adapter, iOS rendszerben a UITableViewDataSource használja. A Google I/O (2024) adatai szerint a RecyclerView az Android alkalmazások 95%-ában használatos listák megjelenítésére, és mindegyik saját Adapter implementációt igényel.

Főbb pontok

  • Adapter — strukturális minta, amely lehetővé teszi az összeférhetetlen interfésszel rendelkező objektumok együttműködését
  • RecyclerView.Adapter — a minta implementációja Androidban, az adatokat ViewHolder-ré alakítja a listához
  • UITableViewDataSource — implementáció iOS-ben, amely az Adapter és DataSource mintákat egyesíti
  • Object Adapter kompozíciót használ: az adapter referenciát tartalmaz az adaptálandó objektumra
  • Class Adapter többszörös öröklést használ — csak C++-ban lehetséges, Java/Swift/Kotlin nyelven nem elérhető

Mi az Adapter minta?

Adapter — strukturális minta, amely megoldja az interfészek összeférhetetlenségének problémáját. Becsomagol egy objektumot (Adaptee) egy osztályba (Adapter) az ügyfél által várt interfésszel (Target). Az ügyfél a Target-tel dolgozik, nem tudva az Adaptee létezéséről. Ez a Wrapper minta egy változata.

Kotlin
// Meglévő osztály összeférhetetlen interfésszel
class LegacyAuthApi {
    fun loginWithToken(token: String): Map {
        return mapOf("status" to "ok", "user_id" to 42)
    }
}

// Cél interfész (amit az ügyfél vár)
interface AuthService {
    suspend fun login(credentials: Credentials): Result
}

// Adapter — a LegacyAuthApi-t AuthService-ré alakítja
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 a login(credentials) hívást loginWithToken(token)-ké alakítja a régi API-hoz. Az ügyfél (ViewModel) az AuthService interfészen keresztül dolgozik, és nem tudja, hogy alatta a legújabb Firebase Auth vagy egy tíz éves régi API van. Ez lehetővé teszi az implementációk cseréjét az ügyfélkód megváltoztatása nélkül.

Adapter Androidban: RecyclerView és ListAdapter

RecyclerView.Adapter — az Adapter minta leggyakoribb implementációja Androidban. Az adatokat (objektumok listáját) ViewHolder-okké alakítja, amelyeket a RecyclerView megjelenít a képernyőn. A ListAdapter (Android Architecture Components) megjelenésével a minta beépített támogatást kapott a különbségszámításhoz a változások animálásához.

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 ListAdapter-t használ DiffUtil-lel a lista hatékony újrarajzolásához. Amikor az adatok frissülnek, a DiffCallback kiszámítja a különbséget a régi és az új lista között, és a RecyclerView csak a megváltozott elemeket animálja — a teljes lista újrarajzolása nélkül. Ez kulcsfontosságú előny a régebbi ListView-vel szemben, amely manuális notifyDataSetChanged()-et igényelt.

Adapter iOS-ben: UITableView és DiffableDataSource

UITableViewDataSource — egyszerre az Adapter és a DataSource minta is. A modell adatait táblázatcellákká alakítja. Az iOS 13-tól kezdve az Apple bevezette a DiffableDataSource-ot — egy modern helyettesítőt, amely az Android ListAdapter-hez hasonlóan automatikusan kiszámítja a változásokat és animálja a frissítéseket.

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

// Modern verzió: 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 DiffableDataSource-szal automatikusan animálja a változásokat az adatok frissítésekor: az elemek sima animációkkal jelennek meg, tűnnek el vagy mozognak. Az iOS DiffableDataSource és az Android ListAdapter ugyanazt a feladatot oldja meg különböző API-kkal, de azonos logikával — különbségszámítás + automatikus animáció.

Object Adapter vs Class Adapter

Object Adapter kompozíciót használ: az adapter referenciát tartalmaz az Adaptee objektumra, és delegálja neki a hívásokat. Class Adapter öröklést használ: az adapter örökli az Adaptee-t, és egyidejűleg implementálja a cél interfészt. Java, Swift és Kotlin nyelven a Class Adapter lehetetlen a többszörös öröklés hiánya miatt — csak az Object Adapter marad.

JellemzőObject AdapterClass Adapter
MechanizmusKompozíció (tartalmazza az Adaptee-t)Öröklés (extends Adaptee)
RugalmasságMagasabb — Adaptee futásidőben változtathatóAlacsonyabb — Adaptee fordításkor rögzített
KapcsoltságGyenge (interfészen keresztül)Erős (öröklésen keresztül)
Alosztályok adaptálásaIgen — Adaptee bármely alosztály lehetNem — csak konkrét szuperosztály
ElérhetőségJava, Kotlin, Swift, C++, C#Csak többszörös örökléssel rendelkező nyelvek (C++)

A mobilfejlesztésben csak Object Adapter használatos. A RecyclerView.Adapter referenciákat tartalmaz az adatokra és a LayoutInflater-re, a UITableViewDataSource elemek tömbjét tartalmazza. A kompozíció rugalmasabbá és tesztelhetőbbé teszi a kódot az örökléshez képest.

Adapter példák valós projektekben

Adapter nem csak listákra alkalmazható. Vizsgáljunk meg három forgatókönyvet a mobilfejlesztés gyakorlatából, ahol a minta megoldja az összeférhetetlen komponensek integrálásának problémáját.

ForgatókönyvAdapteeTargetAdapter
Régi auth könyvtár integrálásaLegacyAuthLib (callback-based)AuthService (suspend)LegacyAuthAdapter
JSON adaptálása új modellhezGsonParserJsonParser (Kotlinx Serialization)GsonAdapter
Egységes interfész push szolgáltatásokhozFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

Minden Adapter beágyazza az átalakítási logikát: a GsonAdapter a JSON karakterláncot objektummá alakítja a @SerializedName segítségével, a PushServiceAdapter elvonatkoztatja a token regisztrációt különböző platformokhoz. Az ügyfél (üzleti logika) egységes interfésszel dolgozik, és nem függ egy konkrét implementációtól.

Gyakori hibák az Adapter implementálásakor

Hibák az Adapter implementálásakor a listák lelassulásához, memóriaszivárgáshoz és adatfrissítési hibákhoz vezetnek. Vizsgáljunk meg három gyakori problémát.

Nehéz műveletek az onBindViewHolder-ban

Az onBindViewHolder minden cellához meghívódik görgetéskor. Objektumok létrehozása, JSON elemzés vagy adatbázis lekérdezések ebben a metódusban a lista akadozásához (frame drops) vezetnek. Megoldás: végezze el az összes nehéz számítást előre, továbbítsa a már elkészített adatokat az adapternek. Képekhez használjon gyorsítótárazó könyvtárakat — Glide, Coil, Kingfisher.

A DiffUtil / DiffableDataSource figyelmen kívül hagyása

A notifyDataSetChanged() minden frissítéskor történő meghívása újrarajzolja a teljes listát, villogást és fókuszvesztést okozva a beviteli mezőkön. Megoldás: használjon ListAdapter-t DiffUtil-lel Androidban vagy DiffableDataSource-t iOS-ben. A különbségszámítás <1 ms-ig tart 1000 elemig terjedő listák esetén, és sima animációkat biztosít.

Nehéz objektumok tárolása az adapterben

Az adapter addig él, amíg a RecyclerView/UITableView. Bitmap vagy nagy tömbök tárolása az adapterben memóriaszivárgáshoz vezet konfigurációváltáskor (képernyő forgatása, téma váltása). Megoldás: az adapter csak könnyű adatmodelleket (data class / struct) tároljon, a nehéz erőforrásokat pedig a ViewHolder-en keresztül igény szerint töltse be.

Gyakran Ismételt Kérdések

Mi a különbség az Adapter és a Proxy között?

Adapter átalakítja az Adaptee interfészét a Target interfészévé — az ügyfél új interakciós módot kap. Proxy ugyanazt az interfészt nyújtja, mint az eredeti objektum, de hozzáférés-vezérlést, gyorsítótárazást vagy lusta inicializálást ad hozzá. A Proxy nem változtatja meg az interfészt, az Adapter — igen.

Miért példa a RecyclerView.Adapter az Adapter mintára?

RecyclerView.Adapter az adatokat (pl. User lista) ViewHolder-ré alakítja, amelyet a RecyclerView megjeleníthet. A RecyclerView ViewHolder-t vár, az adatok List<User> formátumban vannak — az Adapter az egyiket a másikhoz igazítja. Ezenkívül az Adapter kezeli a ViewHolder életciklusát az újrahasznosítási poolon keresztül, javítva a lista teljesítményét.

Mi a különbség az Object Adapter és a Class Adapter között?

Object Adapter kompozíciót használ: referenciát tartalmaz az Adaptee-ra, és delegálja neki a hívásokat. Class Adapter többszörös öröklést használ: örökli az Adaptee-t és implementálja a Target-et. Java, Kotlin és Swift nyelveken csak az Object Adapter érhető el az osztályok többszörös öröklésének hiánya miatt. A Class Adapter csak C++-ban lehetséges.

Mikor használjuk az Adapter-t Facade helyett?

Adapter akkor használatos, amikor két meglévő osztálynak kell együttműködnie az egyik interfészének átalakításával. Facade — amikor egy összetett alrendszerrel való interakciót kell leegyszerűsíteni egy egyszerű interfész biztosításával. Adapter — kompatibilitásért, Facade — egyszerűsítésért.

Megvalósítható az Adapter interfész nélkül?

Igen, lezárások (closures) segítségével. Egy metódussal rendelkező interfész helyett átadható egy függvény. Például Swift-ben: let adapter = { (data: Data) -> [CellConfig] in /* átalakítás */ }. Ezt a megközelítést funkcionális adapternek nevezik. 2+ metódus esetén azonban a protokoll/interfész továbbra is előnyösebb — egyértelművé teszi a szerződést.

Összefoglalás

  • Adapter — strukturális minta, amely az összeférhetetlen Adaptee interfészt egy köztes osztályon keresztül Target-té alakítja
  • RecyclerView.Adapter (Android) és UITableViewDataSource (iOS) — a minta fő példái a mobilfejlesztésben
  • Object Adapter kompozíciót használ; Class Adapter (csak C++) — többszörös öröklést
  • ListAdapter + DiffUtil (Android) és DiffableDataSource (iOS) — modern evolúció automatikus diff-el és animációval
  • Nehéz műveletek onBindViewHolder-ban, a DiffUtil figyelmen kívül hagyása és erőforrások tárolása az adapterben — gyakori hibák
  • Adapter legacy kód integrálására, formátumok adaptálására és különböző implementációk egyesítésére alkalmazható
  • Javaslat: használjon ListAdapter/DiffableDataSource-t a lista frissítések manuális kezelése helyett

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is