Adapter — ano ito, pattern ng pagbabago ng interface para sa mobile development

May-akda: IT Sectr Nai-publish: 2026-02-18 Oras ng pagbabasa: 10 min

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 nagpapahintulot sa mga bagay na may hindi tugmang interface na magtulungan
  • RecyclerView.Adapter — implementasyon ng pattern sa Android, nagbabago ng data sa ViewHolder para sa listahan
  • UITableViewDataSource — implementasyon sa iOS na pinagsasama ang mga pattern ng Adapter at DataSource
  • Object Adapter gumagamit ng komposisyon: ang adapter ay naglalaman ng reference sa inaangkop na bagay
  • Class Adapter gumagamit ng multiple inheritance — posible lamang sa C++, hindi available sa Java/Swift/Kotlin

Ano ang pattern ng Adapter?

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.

Kotlin
// 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.

Adapter sa Android: RecyclerView at ListAdapter

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.

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 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().

Adapter sa iOS: UITableView at DiffableDataSource

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.

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

// 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 vs Class Adapter

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.

KatangianObject AdapterClass Adapter
MekanismoKomposisyon (naglalaman ng Adaptee)Inheritance (extends Adaptee)
FlexibilityMas mataas — ang Adaptee ay maaaring baguhin sa runtimeMas mababa — ang Adaptee ay naayos sa oras ng compilation
PagkakabigkisMahina (sa pamamagitan ng interface)Malakas (sa pamamagitan ng inheritance)
Pag-angkop ng mga subclassOo — ang Adaptee ay maaaring anumang subclassHindi — tanging konkretong superclass
AvailabilityJava, 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.

Mga halimbawa ng Adapter sa totoong proyekto

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.

SenaryoAdapteeTargetAdapter
Pagsasama ng lumang auth libraryLegacyAuthLib (callback-based)AuthService (suspend)LegacyAuthAdapter
Pag-angkop ng JSON sa bagong modeloGsonParserJsonParser (Kotlinx Serialization)GsonAdapter
Pinag-isang interface para sa mga push serviceFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

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 karaniwang pagkakamali sa pag-implementa ng Adapter

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.

Mabibigat na operasyon sa onBindViewHolder

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.

Pagbalewala sa DiffUtil / DiffableDataSource

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.

Pag-iimbak ng mabibigat na bagay sa adapter

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

Ano ang pagkakaiba ng Adapter at Proxy?

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.

Bakit ang RecyclerView.Adapter ay isang halimbawa ng pattern ng Adapter?

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.

Ano ang pagkakaiba ng Object Adapter at Class Adapter?

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++.

Kailan gagamitin ang Adapter sa halip na Facade?

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.

Maaari bang i-implementa ang Adapter nang walang interface?

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

  • Adapter — structural pattern na nagbabago ng hindi tugmang interface ng Adaptee sa Target sa pamamagitan ng isang intermediate na klase
  • RecyclerView.Adapter (Android) at UITableViewDataSource (iOS) — pangunahing mga halimbawa ng pattern sa mobile development
  • Object Adapter gumagamit ng komposisyon; Class Adapter (tanging C++) — multiple inheritance
  • ListAdapter + DiffUtil (Android) at DiffableDataSource (iOS) — modernong ebolusyon na may awtomatikong diff at animasyon
  • Mabibigat na operasyon sa onBindViewHolder, pagbalewala sa DiffUtil, at pag-iimbak ng resources sa adapter — karaniwang pagkakamali
  • Adapter ay inilalapat para sa pagsasama ng legacy code, pag-angkop ng mga format, at pag-iisa ng iba't ibang implementasyon
  • Rekomendasyon: gumamit ng ListAdapter/DiffableDataSource sa halip ng manu-manong pamamahala ng mga update sa listahan

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.

Pag-usapan ang proyekto

Basahin din