Adapter — is een structureel ontwerppatroon dat de interface van de ene klasse omzet naar een andere interface die de client verwacht. In mobiele ontwikkeling wordt Adapter meestal gebruikt in RecyclerView.Adapter voor Android en UITableViewDataSource voor iOS. Volgens Google I/O (2024) wordt RecyclerView gebruikt in 95% van de Android-apps voor het weergeven van lijsten, en elke vereist zijn eigen Adapter-implementatie.
Belangrijkste punten
Adapter — een structureel patroon dat het probleem van incompatibele interfaces oplost. Het wikkelt een object (Adaptee) in een klasse (Adapter) met de interface die de client verwacht (Target). De client werkt met Target, zonder kennis van het bestaan van Adaptee. Dit is een variant van het Wrapper-patroon.
// Bestaande klasse met incompatibele interface
class LegacyAuthApi {
fun loginWithToken(token: String): Map {
return mapOf("status" to "ok", "user_id" to 42)
}
}
// Doelinterface (wat de client verwacht)
interface AuthService {
suspend fun login(credentials: Credentials): Result
}
// Adapter — zet LegacyAuthApi om naar 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 zet de aanroep login(credentials) om naar loginWithToken(token) voor de oude API. De client (ViewModel) werkt via de AuthService-interface en weet niet of er de nieuwste Firebase Auth of een tien jaar oude API onder zit. Dit maakt het mogelijk om implementaties te vervangen zonder de clientcode te wijzigen.
RecyclerView.Adapter — de meest voorkomende implementatie van het Adapter-patroon in Android. Het zet gegevens (een lijst van objecten) om in ViewHolders die RecyclerView op het scherm weergeeft. Met de komst van ListAdapter (Android Architecture Components) kreeg het patroon ingebouwde ondersteuning voor verschilberekening voor animatie van wijzigingen.
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 gebruikt ListAdapter met DiffUtil voor efficiënt hertekenen van de lijst. Wanneer gegevens worden bijgewerkt, berekent DiffCallback het verschil tussen de oude en nieuwe lijst, en RecyclerView animeert alleen de gewijzigde elementen — zonder de hele lijst te hertekenen. Dit is een belangrijk voordeel ten opzichte van de oudere ListView, die handmatig notifyDataSetChanged() vereiste.
UITableViewDataSource — is zowel het Adapter- als het DataSource-patroon. Het zet modelgegevens om in tabelcellen. Vanaf iOS 13 introduceerde Apple DiffableDataSource — een moderne vervanging die, net als ListAdapter in Android, automatisch wijzigingen berekent en updates animeert.
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
}
}
// Moderne versie: 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 met DiffableDataSource animeert automatisch wijzigingen bij het bijwerken van gegevens: elementen verschijnen, verdwijnen of verplaatsen met vloeiende animaties. iOS DiffableDataSource en Android ListAdapter lossen dezelfde taak op met verschillende API's, maar met dezelfde logica — verschilberekening + automatische animatie.
Object Adapter gebruikt compositie: de adapter bevat een verwijzing naar het Adaptee-object en delegeert aanroepen eraan. Class Adapter gebruikt overerving: de adapter erft Adaptee en implementeert tegelijkertijd de doelinterface. In Java, Swift en Kotlin is Class Adapter onmogelijk vanwege het ontbreken van meervoudige overerving — alleen Object Adapter blijft over.
| Kenmerk | Object Adapter | Class Adapter |
|---|---|---|
| Mechanisme | Compositie (bevat Adaptee) | Overerving (extends Adaptee) |
| Flexibiliteit | Hoger — Adaptee kan tijdens runtime worden gewijzigd | Lager — Adaptee staat vast tijdens compilatie |
| Koppeling | Zwak (via interface) | Sterk (via overerving) |
| Aanpassing van subklassen | Ja — Adaptee kan elke subklasse zijn | Nee — alleen concrete superklasse |
| Beschikbaarheid | Java, Kotlin, Swift, C++, C# | Alleen talen met meervoudige overerving (C++) |
In mobiele ontwikkeling wordt alleen Object Adapter gebruikt. RecyclerView.Adapter bevat verwijzingen naar gegevens en LayoutInflater, UITableViewDataSource bevat een array van elementen. Compositie maakt de code flexibeler en beter testbaar in vergelijking met overerving.
Adapter wordt niet alleen voor lijsten toegepast. Laten we drie scenario's uit de praktijk van mobiele ontwikkeling bekijken, waar het patroon het probleem van integratie van incompatibele componenten oplost.
| Scenario | Adaptee | Target | Adapter |
|---|---|---|---|
| Integratie van oude auth-bibliotheek | LegacyAuthLib (callback-based) | AuthService (suspend) | LegacyAuthAdapter |
| Aanpassen JSON aan nieuw model | GsonParser | JsonParser (Kotlinx Serialization) | GsonAdapter |
| Universele interface voor push-diensten | FCM, APNs, Huawei Push | PushService token register | PushServiceAdapter |
Elke Adapter inkapselt de transformatielogica: GsonAdapter vertaalt een JSON-string naar een object via @SerializedName, PushServiceAdapter abstraheert tokenregistratie voor verschillende platforms. De client (businesslogica) werkt met een universele interface en is niet afhankelijk van een specifieke implementatie.
Fouten in de implementatie van Adapter leiden tot trage lijsten, geheugenlekken en bugs met gegevensupdates. Laten we drie veelvoorkomende problemen bekijken.
onBindViewHolder wordt voor elke cel aangeroepen tijdens het scrollen. Objecten maken, JSON-parsen of databasequery's in deze methode leiden tot haperende lijsten (frame drops). Oplossing: voer alle zware berekeningen van tevoren uit en geef de adapter al voorbereide gegevens. Gebruik voor afbeeldingen bibliotheken met caching — Glide, Coil, Kingfisher.
Het aanroepen van notifyDataSetChanged() bij elke update hertekent de hele lijst, wat flikkering en focusverlies op invoervelden veroorzaakt. Oplossing: gebruik ListAdapter met DiffUtil in Android of DiffableDataSource in iOS. Verschilberekening duurt <1 ms voor lijsten tot 1000 elementen en zorgt voor vloeiende animaties.
De adapter leeft zo lang als RecyclerView/UITableView. Bitmap of grote arrays opslaan in de adapter leidt tot geheugenlekken bij configuratiewijzigingen (schermrotatie, themawissel). Oplossing: de adapter mag alleen lichte gegevensmodellen (data class / struct) opslaan, en zware bronnen laden via ViewHolder op aanvraag.
Veelgestelde vragen
Adapter zet de interface van Adaptee om naar de interface van Target — de client krijgt een nieuwe manier van interactie. Proxy biedt dezelfde interface als het originele object, maar voegt toegangscontrole, caching of luie initialisatie toe. Proxy verandert de interface niet, Adapter — wel.
RecyclerView.Adapter zet gegevens (bijv. een lijst van User) om naar ViewHolder die RecyclerView kan weergeven. RecyclerView verwacht ViewHolder, de gegevens hebben de indeling List<User> — Adapter past het een aan het ander aan. Daarnaast beheert Adapter de levenscyclus van ViewHolder via de recycle-pool, wat de prestaties van de lijst verbetert.
Object Adapter gebruikt compositie: bevat een verwijzing naar Adaptee en delegeert aanroepen eraan. Class Adapter gebruikt meervoudige overerving: erft Adaptee en implementeert Target. In Java, Kotlin en Swift is alleen Object Adapter beschikbaar vanwege het ontbreken van meervoudige klasse-overerving. Class Adapter is alleen mogelijk in C++.
Adapter wordt gebruikt wanneer twee bestaande klassen moeten samenwerken door de interface van een van hen te transformeren. Facade — wanneer de interactie met een complex subsysteem vereenvoudigd moet worden door een eenvoudige interface te bieden. Adapter — voor compatibiliteit, Facade — voor vereenvoudiging.
Ja, met behulp van sluitingen (closures). In plaats van een interface met één methode kan een functie worden doorgegeven. Bijvoorbeeld in Swift: let adapter = { (data: Data) -> [CellConfig] in /* transformatie */ }. Deze benadering wordt functionele adapter genoemd. Voor 2+ methoden blijft echter protocol/interface de voorkeur — het maakt het contract expliciet.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook