Adapter — ay isang structural design pattern na nagbabago ng interface ng isang klase sa ibang interface na inaasahan ng client. Sa mobile development, ang Adapter ay kadalasang ginagamit sa RecyclerView.Adapter para sa Android at UITableViewDataSource para sa iOS. Ayon sa data ng Google I/O (2024), ang RecyclerView ay ginagamit sa 95% ng Android apps para sa pagpapakita ng mga listahan, at bawat isa ay nangangailangan ng sarili nitong implementasyon ng Adapter.
Mga Pangunahing Punto
Adapter — structural pattern na lumulutas sa problema ng hindi pagkakatugma ng interface. Binabalot nito ang isang bagay (Adaptee) sa isang klase (Adapter) na may interface na inaasahan ng client (Target). Gumagana ang client sa Target, nang hindi alam ang pagkakaroon ng Adaptee. Ito ay isang variant ng pattern ng Wrapper.
// Umiiral na klase na may hindi tugmang interface
class LegacyAuthApi {
fun loginWithToken(token: String): Map {
return mapOf("status" to "ok", "user_id" to 42)
}
}
// Target na interface (kung ano ang inaasahan ng client)
interface AuthService {
suspend fun login(credentials: Credentials): Result
}
// Adapter — nagbabago ng LegacyAuthApi sa 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 nagbabago ng tawag na login(credentials) sa loginWithToken(token) para sa lumang API. Ang client (ViewModel) ay gumagawa sa pamamagitan ng interface ng AuthService at hindi alam kung ang nasa ilalim nito ay ang pinakabagong Firebase Auth o isang sampung taong gulang na API. Pinapayagan nito ang pagpapalit ng mga implementasyon nang hindi binabago ang code ng client.
RecyclerView.Adapter — ang pinakakaraniwang implementasyon ng pattern ng Adapter sa Android. Binabago nito ang data (listahan ng mga bagay) sa mga ViewHolder na ipinapakita ng RecyclerView sa screen. Sa pagdating ng ListAdapter (Android Architecture Components), nakatanggap ang pattern ng built-in na suporta para sa pagkalkula ng diff para sa animasyon ng mga pagbabago.
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 gumagamit ng ListAdapter na may DiffUtil para sa mahusay na pag-redraw ng listahan. Kapag na-update ang data, kinakalkula ng DiffCallback ang pagkakaiba sa pagitan ng luma at bagong listahan, at ang RecyclerView ay nag-a-animate lamang ng mga binagong elemento — nang hindi nire-redraw ang buong listahan. Ito ang pangunahing bentahe kumpara sa mas lumang ListView na nangangailangan ng manu-manong notifyDataSetChanged().
UITableViewDataSource — ay parehong pattern ng Adapter at DataSource. Binabago nito ang data ng modelo sa mga cell ng talahanayan. Mula noong iOS 13, ipinakilala ng Apple ang DiffableDataSource — isang modernong kapalit na, tulad ng ListAdapter sa Android, ay awtomatikong nagkakalkula ng mga pagbabago at nag-a-animate ng mga update.
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
}
}
// Modernong bersyon: 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 na may DiffableDataSource ay awtomatikong nag-a-animate ng mga pagbabago kapag na-update ang data: ang mga elemento ay lumilitaw, nawawala, o gumagalaw na may maayos na mga animasyon. Ang iOS DiffableDataSource at Android ListAdapter ay lumulutas sa parehong gawain na may iba't ibang API, ngunit may parehong lohika — pagkalkula ng diff + awtomatikong animasyon.
Object Adapter gumagamit ng komposisyon: ang adapter ay naglalaman ng reference sa bagay na Adaptee at nagde-delegate ng mga tawag dito. Class Adapter gumagamit ng inheritance: ang adapter ay nagmamana ng Adaptee at sabay na ini-implementa ang target na interface. Sa Java, Swift, at Kotlin, ang Class Adapter ay imposible dahil sa kawalan ng multiple inheritance — tanging Object Adapter ang natitira.
| Katangian | Object Adapter | Class Adapter |
|---|---|---|
| Mekanismo | Komposisyon (naglalaman ng Adaptee) | Inheritance (extends Adaptee) |
| Flexibility | Mas mataas — ang Adaptee ay maaaring baguhin sa runtime | Mas mababa — ang Adaptee ay naayos sa oras ng compilation |
| Pagkakabigkis | Mahina (sa pamamagitan ng interface) | Malakas (sa pamamagitan ng inheritance) |
| Pag-angkop ng mga subclass | Oo — ang Adaptee ay maaaring anumang subclass | Hindi — tanging konkretong superclass |
| Availability | Java, Kotlin, Swift, C++, C# | Tanging mga wikang may multiple inheritance (C++) |
Sa mobile development, tanging Object Adapter ang ginagamit. Ang RecyclerView.Adapter ay naglalaman ng mga reference sa data at LayoutInflater, ang UITableViewDataSource ay naglalaman ng array ng mga elemento. Ginagawang mas flexible at testable ng komposisyon ang code kumpara sa inheritance.
Adapter ay hindi lamang inilalapat para sa mga listahan. Tingnan natin ang tatlong senaryo mula sa praktika ng mobile development kung saan nilulutas ng pattern ang problema ng pagsasama ng mga hindi tugmang bahagi.
| Senaryo | Adaptee | Target | Adapter |
|---|---|---|---|
| Pagsasama ng lumang auth library | LegacyAuthLib (callback-based) | AuthService (suspend) | LegacyAuthAdapter |
| Pag-angkop ng JSON sa bagong modelo | GsonParser | JsonParser (Kotlinx Serialization) | GsonAdapter |
| Pinag-isang interface para sa mga push service | FCM, APNs, Huawei Push | PushService token register | PushServiceAdapter |
Bawat Adapter ay nag-e-encapsulate ng lohika ng pagbabago: Isinasalin ng GsonAdapter ang JSON string sa isang bagay sa pamamagitan ng @SerializedName, ina-abstrack ng PushServiceAdapter ang pagpaparehistro ng token para sa iba't ibang platform. Ang client (business logic) ay gumagana sa isang pinag-isang interface at hindi nakadepende sa isang partikular na implementasyon.
Mga pagkakamali sa pag-implementa ng Adapter ay humahantong sa pagbagal ng mga listahan, pagtagas ng memorya, at mga bug sa pag-update ng data. Talakayin natin ang tatlong karaniwang problema.
Ang onBindViewHolder ay tinatawag para sa bawat cell habang nag-s-scroll. Paglikha ng mga bagay, pag-parse ng JSON, o pag-query sa database sa pamamaraang ito ay humahantong sa pagsablay ng listahan (frame drops). Solusyon: gawin ang lahat ng mabibigat na pagkalkula nang maaga, ipasa ang handa nang data sa adapter. Para sa mga larawan, gumamit ng mga library na may caching — Glide, Coil, Kingfisher.
Ang pagtawag ng notifyDataSetChanged() sa bawat update ay nagre-redraw ng buong listahan, na nagdudulot ng pagkutitap at pagkawala ng focus sa mga input field. Solusyon: gumamit ng ListAdapter na may DiffUtil sa Android o DiffableDataSource sa iOS. Ang pagkalkula ng diff ay tumatagal ng <1 ms para sa mga listahan hanggang 1000 elemento at tinitiyak ang maayos na mga animasyon.
Ang adapter ay nabubuhay hangga't ang RecyclerView/UITableView. Pag-iimbak ng Bitmap o malalaking array sa adapter ay humahantong sa pagtagas ng memorya sa pagbabago ng configuration (pag-ikot ng screen, pagbabago ng tema). Solusyon: ang adapter ay dapat mag-imbak lamang ng mga magaan na modelo ng data (data class / struct), at mag-load ng mabibigat na resources sa pamamagitan ng ViewHolder kapag kinakailangan.
Mga Madalas Itanong
Adapter ay nagbabago ng interface ng Adaptee sa interface ng Target — ang client ay nakakakuha ng bagong paraan ng pakikipag-ugnayan. Proxy ay nagbibigay ng parehong interface gaya ng orihinal na bagay, ngunit nagdaragdag ng kontrol sa pag-access, caching, o lazy initialization. Ang Proxy ay hindi nagbabago ng interface, ang Adapter — nagbabago.
RecyclerView.Adapter ay nagbabago ng data (halimbawa, listahan ng User) sa ViewHolder na maaaring ipakita ng RecyclerView. Ang RecyclerView ay umaasa ng ViewHolder, ang data ay nasa format na List<User> — iniangkop ng Adapter ang isa sa isa pa. Bukod pa rito, pinamamahalaan ng Adapter ang lifecycle ng ViewHolder sa pamamagitan ng recycle pool, pinapabuti ang performance ng listahan.
Object Adapter gumagamit ng komposisyon: naglalaman ng reference sa Adaptee at nagde-delegate ng mga tawag dito. Class Adapter gumagamit ng multiple inheritance: nagmamana ng Adaptee at nag-i-implementa ng Target. Sa Java, Kotlin, at Swift, tanging Object Adapter ang available dahil sa kawalan ng multiple inheritance ng klase. Ang Class Adapter ay posible lamang sa C++.
Adapter ay ginagamit kapag kailangang pagtulungin ang dalawang umiiral na klase sa pamamagitan ng pagbabago ng interface ng isa sa kanila. Facade — kapag kailangang pasimplehin ang pakikipag-ugnayan sa isang kumplikadong subsystem sa pamamagitan ng pagbibigay ng simpleng interface. Adapter — para sa compatibility, Facade — para sa pagpapasimple.
Oo, gamit ang mga closure. Sa halip na isang interface na may isang pamamaraan, maaaring magpasa ng isang function. Halimbawa, sa Swift: let adapter = { (data: Data) -> [CellConfig] in /* pagbabago */ }. Ang ganitong approach ay tinatawag na functional adapter. Gayunpaman para sa 2+ pamamaraan, ang protocol/interface ay nananatiling mas gusto — ginagawa nitong tahasan ang kontrata.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din