Adapter — co to jest, wzorzec przekształcania interfejsów dla programowania mobilnego

Autor: IT Sectr Opublikowano: 2026-02-18 Czas czytania: 10 min

Adapter — to strukturalny wzorzec projektowy, który przekształca interfejs jednej klasy na inny interfejs oczekiwany przez klienta. W programowaniu mobilnym Adapter najczęściej używany jest w RecyclerView.Adapter dla Android i UITableViewDataSource dla iOS. Według danych Google I/O (2024), RecyclerView jest używany w 95% aplikacji Android do wyświetlania list, a każda wymaga własnej implementacji Adapter.

Najważniejsze

  • Adapter — wzorzec strukturalny umożliwiający współpracę obiektów z niekompatybilnymi interfejsami
  • RecyclerView.Adapter — implementacja wzorca w Android, przekształcająca dane w ViewHolder dla listy
  • UITableViewDataSource — implementacja w iOS łącząca wzorce Adapter i DataSource
  • Object Adapter wykorzystuje kompozycję: adapter zawiera odniesienie do adaptowanego obiektu
  • Class Adapter wykorzystuje wielodziedziczenie — możliwe tylko w C++, niedostępne w Java/Swift/Kotlin

Czym jest wzorzec Adapter?

Adapter — wzorzec strukturalny rozwiązujący problem niekompatybilności interfejsów. Opakowuje jeden obiekt (Adaptee) w klasę (Adapter) z interfejsem oczekiwanym przez klienta (Target). Klient pracuje z Target, nie wiedząc o istnieniu Adaptee. Jest to odmiana wzorca Wrapper.

Kotlin
// Istniejąca klasa z niekompatybilnym interfejsem
class LegacyAuthApi {
    fun loginWithToken(token: String): Map {
        return mapOf("status" to "ok", "user_id" to 42)
    }
}

// Docelowy interfejs (czego oczekuje klient)
interface AuthService {
    suspend fun login(credentials: Credentials): Result
}

// Adapter — przekształca LegacyAuthApi w 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 przekształca wywołanie login(credentials) w loginWithToken(token) dla starego API. Klient (ViewModel) pracuje przez interfejs AuthService i nie wie, że pod spodem znajduje się najnowszy Firebase Auth lub legacy API sprzed dziesięciu lat. Pozwala to wymieniać implementacje bez zmiany kodu klienta.

Adapter w Android: RecyclerView i ListAdapter

RecyclerView.Adapter — najczęstsza implementacja wzorca Adapter w Android. Przekształca dane (listę obiektów) w ViewHolder'y, które RecyclerView wyświetla na ekranie. Wraz z pojawieniem się ListAdapter (Android Architecture Components) wzorzec otrzymał wbudowane wsparcie obliczania różnic do animacji zmian.

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 używa ListAdapter z DiffUtil do efektywnego przerysowywania listy. Gdy dane są aktualizowane, DiffCallback oblicza różnicę między starą a nową listą, a RecyclerView animuje tylko zmienione elementy — bez przerysowywania całej listy. To kluczowa zaleta w porównaniu ze starszym ListView, który wymagał ręcznego notifyDataSetChanged().

Adapter w iOS: UITableView i DiffableDataSource

UITableViewDataSource — to jednocześnie wzorzec Adapter i DataSource. Przekształca dane modelu w komórki tabeli. Od iOS 13 Apple wprowadziło DiffableDataSource — nowoczesny zamiennik, który podobnie jak ListAdapter w Android, automatycznie oblicza zmiany i animuje aktualizacje.

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

// Nowoczesna wersja: 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 z DiffableDataSource automatycznie animuje zmiany przy aktualizacji danych: elementy pojawiają się, znikają lub przesuwają z płynnymi animacjami. iOS DiffableDataSource i Android ListAdapter rozwiązują to samo zadanie różnymi API, ale według tej samej logiki — obliczanie różnic + automatyczna animacja.

Object Adapter vs Class Adapter

Object Adapter wykorzystuje kompozycję: adapter zawiera odniesienie do obiektu Adaptee i deleguje mu wywołania. Class Adapter wykorzystuje dziedziczenie: adapter dziedziczy Adaptee i jednocześnie implementuje docelowy interfejs. W Java, Swift i Kotlin Class Adapter jest niemożliwy z powodu braku wielodziedziczenia — pozostaje tylko Object Adapter.

CechaObject AdapterClass Adapter
MechanizmKompozycja (zawiera Adaptee)Dziedziczenie (extends Adaptee)
ElastycznośćWiększa — Adaptee można zmieniać w runtimeMniejsza — Adaptee jest stały w czasie kompilacji
PowiazanieSłabe (przez interfejs)Silne (przez dziedziczenie)
Adaptacja podklasTak — Adaptee może być dowolną podklasąNie — tylko konkretna superklasa
DostępnośćJava, Kotlin, Swift, C++, C#Tylko języki z wielodziedziczeniem (C++)

W programowaniu mobilnym używany jest tylko Object Adapter. RecyclerView.Adapter zawiera odniesienia do danych i LayoutInflater, UITableViewDataSource zawiera tablicę elementów. Kompozycja czyni kod bardziej elastycznym i testowalnym w porównaniu z dziedziczeniem.

Przykłady Adapter w rzeczywistych projektach

Adapter jest stosowany nie tylko do list. Rozważmy trzy scenariusze z praktyki programowania mobilnego, gdzie wzorzec rozwiązuje problem integracji niekompatybilnych komponentów.

ScenariuszAdapteeTargetAdapter
Integracja starej biblioteki authLegacyAuthLib (callback-based)AuthService (suspend)LegacyAuthAdapter
Adaptacja JSON pod nowy modelGsonParserJsonParser (Kotlinx Serialization)GsonAdapter
Jednolity interfejs dla push-serwisówFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

Każdy Adapter hermetyzuje logikę przekształcania: GsonAdapter tłumaczy JSON-string na obiekt przez @SerializedName, PushServiceAdapter abstrahuje rejestrację tokena dla różnych platform. Klient (logika biznesowa) pracuje z jednolitym interfejsem i nie zależy od konkretnej implementacji.

Typowe błędy przy implementacji Adapter

Błędy w implementacji Adapter prowadzą do spowolnienia list, wycieków pamięci i błędów aktualizacji danych. Omówimy trzy częste problemy.

Ciężkie operacje w onBindViewHolder

onBindViewHolder jest wywoływane dla każdej komórki podczas przewijania. Tworzenie obiektów, parsowanie JSON lub zapytania do bazy danych w tej metodzie prowadzą do zacięć listy (frame drops). Rozwiązanie: wszystkie ciężkie obliczenia wykonywać z wyprzedzeniem, przekazując do adaptera już gotowe dane. Dla obrazów używać bibliotek z buforowaniem — Glide, Coil, Kingfisher.

Ignorowanie DiffUtil / DiffableDataSource

Wywołanie notifyDataSetChanged() przy każdej aktualizacji przerysowuje całą listę, powodując migotanie i utratę fokusu na polach wprowadzania. Rozwiązanie: używać ListAdapter z DiffUtil w Android lub DiffableDataSource w iOS. Obliczanie różnic zajmuje <1 ms dla list do 1000 elementów i zapewnia płynne animacje.

Przechowywanie ciężkich obiektów w adapterze

Adapter żyje tak długo, jak RecyclerView/UITableView. Przechowywanie Bitmap lub dużych tablic w adapterze prowadzi do wycieków pamięci przy zmianie konfiguracji (obrót ekranu, zmiana motywu). Rozwiązanie: adapter powinien przechowywać tylko lekkie modele danych (data class / struct), a ciężkie zasoby ładować przez ViewHolder na żądanie.

Często zadawane pytania

Jaka jest różnica między Adapter a Proxy?

Adapter przekształca interfejs Adaptee na interfejs Target — klient otrzymuje nowy sposób interakcji. Proxy udostępnia ten sam interfejs co oryginalny obiekt, ale dodaje kontrolę dostępu, buforowanie lub leniwą inicjalizację. Proxy nie zmienia interfejsu, Adapter — zmienia.

Dlaczego RecyclerView.Adapter to przykład wzorca Adapter?

RecyclerView.Adapter przekształca dane (np. listę User) w ViewHolder, który RecyclerView może wyświetlić. RecyclerView oczekuje ViewHolder, dane mają format List<User> — Adapter dopasowuje jedno do drugiego. Dodatkowo Adapter zarządza cyklem życia ViewHolder przez recycle-pulę, zwiększając wydajność listy.

Jaka jest różnica między Object Adapter a Class Adapter?

Object Adapter wykorzystuje kompozycję: zawiera odniesienie do Adaptee i deleguje mu wywołania. Class Adapter wykorzystuje wielodziedziczenie: dziedziczy Adaptee i implementuje Target. W Java, Kotlin i Swift dostępny jest tylko Object Adapter z powodu braku wielodziedziczenia klas. Class Adapter jest możliwy tylko w C++.

Kiedy używać Adapter zamiast Facade?

Adapter jest używany, gdy trzeba sprawić, by dwie istniejące klasy działały razem, przekształcając interfejs jednej z nich. Facade — gdy trzeba uprościć interakcję ze złożonym podsystemem, udostępniając prosty interfejs. Adapter — dla kompatybilności, Facade — dla uproszczenia.

Czy można zaimplementować Adapter bez interfejsu?

Tak, za pomocą domknięć. Zamiast interfejsu z jedną metodą można przekazać funkcję. Na przykład w Swift: let adapter = { (data: Data) -> [CellConfig] in /* przekształcanie */ }. Takie podejście nazywa się functional adapter. Jednak dla 2+ metod protokół/interfejs pozostaje preferowany — czyni kontrakt jawnym.

Podsumowanie

  • Adapter — wzorzec strukturalny przekształcający niekompatybilny interfejs Adaptee w Target poprzez klasę pośredniczącą
  • RecyclerView.Adapter (Android) i UITableViewDataSource (iOS) — główne przykłady wzorca w programowaniu mobilnym
  • Object Adapter wykorzystuje kompozycję; Class Adapter (tylko C++) — wielodziedziczenie
  • ListAdapter + DiffUtil (Android) i DiffableDataSource (iOS) — nowoczesne ewolucje z automatycznym obliczaniem różnic i animacją
  • Ciężkie operacje w onBindViewHolder, ignorowanie DiffUtil i przechowywanie zasobów w adapterze — częste błędy
  • Adapter jest stosowany do integracji legacy-kodu, adaptacji formatów i unifikacji różnych implementacji
  • Zalecenie: używaj ListAdapter/DiffableDataSource zamiast ręcznego zarządzania aktualizacjami listy

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również