Adapter — co to je, vzor převodu rozhraní pro mobilní vývoj

Autor: IT Sectr Publikováno: 2026-02-18 Doba čtení: 10 min

Adapter — je strukturální návrhový vzor, který převádí rozhraní jedné třídy na jiné rozhraní očekávané klientem. V mobilním vývoji se Adapter nejčastěji používá v RecyclerView.Adapter pro Android a UITableViewDataSource pro iOS. Podle údajů Google I/O (2024) se RecyclerView používá v 95% Android aplikací pro zobrazení seznamů a každá vyžaduje vlastní implementaci Adapteru.

Hlavní body

  • Adapter — strukturální vzor umožňující objektům s nekompatibilními rozhraními spolupracovat
  • RecyclerView.Adapter — implementace vzoru v Android, převádí data na ViewHolder pro seznam
  • UITableViewDataSource — implementace v iOS, kombinující vzory Adapter a DataSource
  • Object Adapter používá kompozici: adaptér obsahuje odkaz na adaptovaný objekt
  • Class Adapter používá vícenásobnou dědičnost — možný pouze v C++, nedostupný v Java/Swift/Kotlin

Co je vzor Adapter?

Adapter — strukturální vzor, který řeší problém nekompatibility rozhraní. Zabalí jeden objekt (Adaptee) do třídy (Adapter) s rozhraním očekávaným klientem (Target). Klient pracuje s Target, aniž by věděl o existenci Adaptee. Toto je varianta vzoru Wrapper.

Kotlin
// Existující třída s nekompatibilním rozhraním
class LegacyAuthApi {
    fun loginWithToken(token: String): Map {
        return mapOf("status" to "ok", "user_id" to 42)
    }
}

// Cílové rozhraní (co klient očekává)
interface AuthService {
    suspend fun login(credentials: Credentials): Result
}

// Adapter — převádí LegacyAuthApi na 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 převádí volání login(credentials) na loginWithToken(token) pro staré API. Klient (ViewModel) pracuje přes rozhraní AuthService a neví, zda je pod ním nejnovější Firebase Auth nebo deset let staré API. To umožňuje výměnu implementací bez změny klientského kódu.

Adapter v Android: RecyclerView a ListAdapter

RecyclerView.Adapter — nejrozšířenější implementace vzoru Adapter v Android. Převádí data (seznam objektů) na ViewHoldery, které RecyclerView zobrazuje na obrazovce. S příchodem ListAdapter (Android Architecture Components) získal vzor vestavěnou podporu výpočtu rozdílů pro animaci změn.

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 používá ListAdapter s DiffUtil pro efektivní překreslování seznamu. Když jsou data aktualizována, DiffCallback vypočítá rozdíl mezi starým a novým seznamem a RecyclerView animuje pouze změněné prvky — bez překreslení celého seznamu. To je klíčová výhoda oproti staršímu ListView, který vyžadoval ruční notifyDataSetChanged().

Adapter v iOS: UITableView a DiffableDataSource

UITableViewDataSource — je současně vzor Adapter i DataSource. Převádí data modelu na buňky tabulky. Od iOS 13 Apple představil DiffableDataSource — moderní náhradu, která podobně jako ListAdapter v Android automaticky vypočítává změny a animuje aktualizace.

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í verze: 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 s DiffableDataSource automaticky animuje změny při aktualizaci dat: prvky se objevují, mizí nebo přesouvají s plynulými animacemi. iOS DiffableDataSource a Android ListAdapter řeší stejný úkol různými API, ale stejnou logikou — výpočet rozdílů + automatická animace.

Object Adapter vs Class Adapter

Object Adapter používá kompozici: adaptér obsahuje odkaz na objekt Adaptee a deleguje mu volání. Class Adapter používá dědičnost: adaptér dědí Adaptee a současně implementuje cílové rozhraní. V Java, Swift a Kotlin je Class Adapter nemožný kvůli chybějící vícenásobné dědičnosti — zbývá pouze Object Adapter.

VlastnostObject AdapterClass Adapter
MechanismusKompozice (obsahuje Adaptee)Dědičnost (extends Adaptee)
FlexibilitaVyšší — Adaptee lze měnit za běhuNižší — Adaptee je fixní při kompilaci
ProvázanostSlabá (přes rozhraní)Silná (přes dědičnost)
Adaptace podtřídAno — Adaptee může být libovolná podtřídaNe — pouze konkrétní nadtřída
DostupnostJava, Kotlin, Swift, C++, C#Pouze jazyky s vícenásobnou dědičností (C++)

V mobilním vývoji se používá pouze Object Adapter. RecyclerView.Adapter obsahuje odkazy na data a LayoutInflater, UITableViewDataSource obsahuje pole prvků. Kompozice činí kód flexibilnějším a testovatelnějším ve srovnání s dědičností.

Příklady Adapteru v reálných projektech

Adapter se aplikuje nejen pro seznamy. Podívejme se na tři scénáře z praxe mobilního vývoje, kde vzor řeší problém integrace nekompatibilních komponent.

ScénářAdapteeTargetAdapter
Integrace staré auth knihovnyLegacyAuthLib (callback-based)AuthService (suspend)LegacyAuthAdapter
Adaptace JSON pod nový modelGsonParserJsonParser (Kotlinx Serialization)GsonAdapter
Jednotné rozhraní pro push službyFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

Každý Adapter zapouzdřuje logiku převodu: GsonAdapter překládá JSON řetězec na objekt přes @SerializedName, PushServiceAdapter abstrahuje registraci tokenu pro různé platformy. Klient (business logika) pracuje s jednotným rozhraním a nezávisí na konkrétní implementaci.

Typické chyby při implementaci Adapteru

Chyby v implementaci Adapteru vedou ke zpomalení seznamů, únikům paměti a chybám při aktualizaci dat. Rozebereme tři časté problémy.

Těžké operace v onBindViewHolder

onBindViewHolder je volán pro každou buňku při scrollování. Vytváření objektů, parsování JSON nebo dotazy do databáze v této metodě vedou k trhání seznamu (frame drops). Řešení: všechny těžké výpočty provádět předem, předávat adaptéru již hotová data. Pro obrázky používat knihovny s cachingem — Glide, Coil, Kingfisher.

Ignorování DiffUtil / DiffableDataSource

Volání notifyDataSetChanged() při každé aktualizaci překreslí celý seznam, způsobuje blikání a ztrátu fokusu na vstupních polích. Řešení: používat ListAdapter s DiffUtil v Android nebo DiffableDataSource v iOS. Výpočet rozdílů trvá <1 ms pro seznamy do 1000 prvků a zajišťuje plynulé animace.

Ukládání těžkých objektů v adaptéru

Adaptér žije stejně dlouho jako RecyclerView/UITableView. Ukládání Bitmap nebo velkých polí v adaptéru vede k únikům paměti při změně konfigurace (otočení obrazovky, změna motivu). Řešení: adaptér by měl ukládat pouze lehké datové modely (data class / struct) a těžké zdroje načítat přes ViewHolder na vyžádání.

Často kladené otázky

Jaký je rozdíl mezi Adapter a Proxy?

Adapter převádí rozhraní Adaptee na rozhraní Target — klient získává nový způsob interakce. Proxy poskytuje stejné rozhraní jako původní objekt, ale přidává řízení přístupu, caching nebo línou inicializaci. Proxy nemění rozhraní, Adapter — mění.

Proč je RecyclerView.Adapter příkladem vzoru Adapter?

RecyclerView.Adapter převádí data (např. seznam User) na ViewHolder, který RecyclerView může zobrazit. RecyclerView očekává ViewHolder, data jsou ve formátu List<User> — Adapter přizpůsobuje jedno druhému. Navíc Adapter spravuje životní cyklus ViewHolder přes recyklační pool, čímž zvyšuje výkon seznamu.

Jaký je rozdíl mezi Object Adapter a Class Adapter?

Object Adapter používá kompozici: obsahuje odkaz na Adaptee a deleguje mu volání. Class Adapter používá vícenásobnou dědičnost: dědí Adaptee a implementuje Target. V Java, Kotlin a Swift je dostupný pouze Object Adapter kvůli chybějící vícenásobné dědičnosti tříd. Class Adapter je možný pouze v C++.

Kdy použít Adapter místo Facade?

Adapter se používá, když dvě existující třídy potřebují spolupracovat převedením rozhraní jedné z nich. Facade — když je potřeba zjednodušit interakci se složitým subsystémem poskytnutím jednoduchého rozhraní. Adapter — pro kompatibilitu, Facade — pro zjednodušení.

Lze implementovat Adapter bez rozhraní?

Ano, pomocí uzávěrů (closures). Místo rozhraní s jednou metodou lze předat funkci. Například ve Swift: let adapter = { (data: Data) -> [CellConfig] in /* převod */ }. Tento přístup se nazývá functional adapter. Pro 2+ metod však protokol/rozhraní zůstává preferovaný — činí kontrakt explicitním.

Shrnutí

  • Adapter — strukturální vzor převádějící nekompatibilní rozhraní Adaptee na Target prostřednictvím zprostředkující třídy
  • RecyclerView.Adapter (Android) a UITableViewDataSource (iOS) — hlavní příklady vzoru v mobilním vývoji
  • Object Adapter používá kompozici; Class Adapter (pouze C++) — vícenásobnou dědičnost
  • ListAdapter + DiffUtil (Android) a DiffableDataSource (iOS) — moderní evoluce s automatickým diffem a animací
  • Těžké operace v onBindViewHolder, ignorování DiffUtil a ukládání zdrojů v adaptéru — časté chyby
  • Adapter se aplikuje pro integraci legacy kódu, adaptaci formátů a unifikaci různých implementací
  • Doporučení: používejte ListAdapter/DiffableDataSource místo ručního řízení aktualizací seznamu

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také