Adapter — คืออะไร, รูปแบบการแปลงอินเทอร์เฟซสำหรับการพัฒนาแอปพลิเคชันมือถือ

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-02-18 เวลาอ่าน: 10 นาที

Adapter คือรูปแบบการออกแบบเชิงโครงสร้างที่แปลงอินเทอร์เฟซของคลาสหนึ่งไปเป็นอีกอินเทอร์เฟซหนึ่งที่ไคลเอ็นต์คาดหวัง ในการพัฒนาแอปพลิเคชันมือถือ Adapter มักถูกใช้ใน RecyclerView.Adapter สำหรับ Android และ UITableViewDataSource สำหรับ iOS ตามข้อมูลจาก Google I/O (2024) RecyclerView ถูกใช้ใน 95% ของแอปพลิเคชัน Android เพื่อแสดงรายการ และแต่ละแอปพลิเคชันต้องการการใช้งาน Adapter ของตัวเอง

ประเด็นสำคัญ

  • Adapter คือรูปแบบเชิงโครงสร้างที่ช่วยให้ออบเจ็กต์ที่มีอินเทอร์เฟซที่เข้ากันไม่ได้ทำงานร่วมกันได้
  • RecyclerView.Adapter คือการใช้งานรูปแบบใน Android ที่แปลงข้อมูลเป็น ViewHolder สำหรับรายการ
  • UITableViewDataSource คือการใช้งานใน iOS ที่รวมรูปแบบ Adapter และ DataSource เข้าด้วยกัน
  • Object Adapter ใช้การประกอบ: อะแดปเตอร์เก็บการอ้างอิงถึงออบเจ็กต์ที่ถูกปรับ
  • Class Adapter ใช้การสืบทอดหลายคลาส — เป็นไปได้เฉพาะใน C++ ไม่พร้อมใช้งานใน Java/Swift/Kotlin

รูปแบบ Adapter คืออะไร?

Adapter คือรูปแบบเชิงโครงสร้างที่แก้ปัญหาความไม่เข้ากันของอินเทอร์เฟซ มันห่อหุ้มออบเจ็กต์หนึ่ง (Adaptee) ไว้ในคลาส (Adapter) ที่มีอินเทอร์เฟซที่ไคลเอ็นต์คาดหวัง (Target) ไคลเอ็นต์ทำงานกับ Target โดยไม่รู้ถึงการมีอยู่ของ Adaptee นี่คือรูปแบบหนึ่งของ Wrapper

Kotlin
// คลาสที่มีอยู่กับอินเทอร์เฟซที่เข้ากันไม่ได้
class LegacyAuthApi {
    fun loginWithToken(token: String): Map {
        return mapOf("status" to "ok", "user_id" to 42)
    }
}

// อินเทอร์เฟซเป้าหมาย (สิ่งที่ไคลเอ็นต์คาดหวัง)
interface AuthService {
    suspend fun login(credentials: Credentials): Result
}

// Adapter — แปลง LegacyAuthApi เป็น 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 แปลงการเรียก login(credentials) เป็น loginWithToken(token) สำหรับ API เก่า ไคลเอ็นต์ (ViewModel) ทำงานผ่านอินเทอร์เฟซ AuthService และไม่รู้ว่าภายใต้คือ Firebase Auth ล่าสุดหรือ API เก่าอายุสิบปี สิ่งนี้ช่วยให้เปลี่ยนการใช้งานได้โดยไม่ต้องแก้ไขโค้ดไคลเอ็นต์

Adapter ใน Android: RecyclerView และ ListAdapter

RecyclerView.Adapter คือการใช้งานรูปแบบ Adapter ที่พบบ่อยที่สุดใน Android มันแปลงข้อมูล (รายการออบเจ็กต์) เป็น ViewHolder ที่ RecyclerView แสดงบนหน้าจอ ด้วยการเปิดตัว ListAdapter (Android Architecture Components) รูปแบบได้รับการสนับสนุนการคำนวณความแตกต่างในตัวสำหรับการเคลื่อนไหวของการเปลี่ยนแปลง

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 ใช้ ListAdapter กับ DiffUtil เพื่อการวาดรายการใหม่ที่มีประสิทธิภาพ เมื่อข้อมูลถูกอัปเดต DiffCallback คำนวณความแตกต่างระหว่างรายการเก่าและใหม่ และ RecyclerView เคลื่อนไหวเฉพาะรายการที่เปลี่ยนไป — โดยไม่ต้องวาดรายการทั้งหมดใหม่ นี่คือข้อได้เปรียบหลักเหนือ ListView แบบเก่าที่ต้องการ notifyDataSetChanged() ด้วยตนเอง

Adapter ใน iOS: UITableView และ DiffableDataSource

UITableViewDataSource เป็นทั้งรูปแบบ Adapter และรูปแบบ DataSource ในเวลาเดียวกัน มันแปลงข้อมูลโมเดลเป็นเซลล์ตาราง ตั้งแต่ iOS 13 Apple ได้แนะนำ DiffableDataSource — ตัวแทนที่ทันสมัย ซึ่งเหมือนกับ ListAdapter ใน Android ที่คำนวณการเปลี่ยนแปลงโดยอัตโนมัติและเคลื่อนไหวการอัปเดต

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

// เวอร์ชันทันสมัย: 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 กับ DiffableDataSource จะเคลื่อนไหวการเปลี่ยนแปลงโดยอัตโนมัติเมื่อข้อมูลถูกอัปเดต: รายการปรากฏ หายไป หรือเคลื่อนที่ด้วยแอนิเมชันที่ราบรื่น DiffableDataSource ของ iOS และ ListAdapter ของ Android แก้ปัญหาเดียวกันด้วย API ที่แตกต่างกันแต่ใช้ตรรกะเดียวกัน — การคำนวณความแตกต่าง + แอนิเมชันอัตโนมัติ

Object Adapter เปรียบเทียบกับ Class Adapter

Object Adapter ใช้การประกอบ: อะแดปเตอร์เก็บการอ้างอิงถึงออบเจ็กต์ Adaptee และมอบหมายการเรียกไปยังมัน Class Adapter ใช้การสืบทอด: อะแดปเตอร์สืบทอดจาก Adaptee และใช้อินเทอร์เฟซเป้าหมายพร้อมกัน ใน Java, Swift และ Kotlin Class Adapter เป็นไปไม่ได้เนื่องจากขาดการสืบทอดหลายคลาส — เหลือเพียง Object Adapter

คุณลักษณะObject AdapterClass Adapter
กลไกการประกอบ (ประกอบด้วย Adaptee)การสืบทอด (ขยาย Adaptee)
ความยืดหยุ่นสูงกว่า — Adaptee สามารถเปลี่ยนได้ที่รันไทม์ต่ำกว่า — Adaptee ถูกกำหนดที่เวลาคอมไพล์
การเชื่อมโยงอ่อน (ผ่านอินเทอร์เฟซ)แข็งแกร่ง (ผ่านการสืบทอด)
การปรับคลาสย่อยได้ — Adaptee สามารถเป็นคลาสย่อยใดก็ได้ไม่ได้ — เฉพาะซูเปอร์คลาสที่กำหนด
ความพร้อมใช้งานJava, Kotlin, Swift, C++, C#เฉพาะภาษาที่มีการสืบทอดหลายคลาส (C++)

ในการพัฒนาแอปพลิเคชันมือถือ ใช้เฉพาะ Object Adapter เท่านั้น RecyclerView.Adapter เก็บการอ้างอิงถึงข้อมูลและ LayoutInflater, UITableViewDataSource เก็บอาร์เรย์ของรายการ การประกอบทำให้โค้ดยืดหยุ่นและทดสอบได้มากขึ้นเมื่อเทียบกับการสืบทอด

ตัวอย่าง Adapter ในโปรเจกต์จริง

Adapter ไม่ได้ใช้เฉพาะสำหรับรายการเท่านั้น มาพิจารณาสามสถานการณ์จากการฝึกปฏิบัติพัฒนาแอปพลิเคชันมือถือที่รูปแบบแก้ปัญหาการรวมคอมโพเนนต์ที่เข้ากันไม่ได้

สถานการณ์AdapteeTargetAdapter
การรวมไลบรารีตรวจสอบสิทธิ์เก่าLegacyAuthLib (แบบ callback)AuthService (suspend)LegacyAuthAdapter
การปรับ JSON ให้เข้ากับโมเดลใหม่GsonParserJsonParser (Kotlinx Serialization)GsonAdapter
อินเทอร์เฟซรวมสำหรับบริการ PushFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

แต่ละ Adapter ห่อหุ้มตรรกะการแปลง: GsonAdapter แปลงสตริง JSON เป็นออบเจ็กต์ผ่าน @SerializedName, PushServiceAdapter ทำให้การลงทะเบียนโทเค็นเป็นนามธรรมสำหรับแพลตฟอร์มต่างๆ ไคลเอ็นต์ (ตรรกะทางธุรกิจ) ทำงานกับอินเทอร์เฟซรวมและไม่ขึ้นอยู่กับการใช้งานเฉพาะ

ข้อผิดพลาดทั่วไปเมื่อใช้งาน Adapter

ข้อผิดพลาดในการใช้งาน Adapter นำไปสู่ความช้าของรายการ หน่วยความจำรั่ว และบั๊กกับการอัปเดตข้อมูล มาดูปัญหาทั่วไปสามอย่าง

การดำเนินการหนักใน onBindViewHolder

onBindViewHolder ถูกเรียกสำหรับแต่ละเซลล์ระหว่างการเลื่อน การสร้างออบเจ็กต์ การแยกวิเคราะห์ JSON หรือการเรียกฐานข้อมูลในเมธอดนี้นำไปสู่การกระตุกของเฟรม วิธีแก้: ดำเนินการคำนวณหนักทั้งหมดล่วงหน้า ส่งข้อมูลที่พร้อมแล้วให้อะแดปเตอร์ ใช้ไลบรารีแคชสำหรับรูปภาพ — Glide, Coil, Kingfisher

การละเว้น DiffUtil / DiffableDataSource

การเรียก notifyDataSetChanged() ทุกครั้งที่อัปเดตจะวาดรายการทั้งหมดใหม่ ทำให้เกิดการกะพริบและสูญเสียโฟกัสบนอินพุต วิธีแก้: ใช้ ListAdapter กับ DiffUtil ใน Android หรือ DiffableDataSource ใน iOS การคำนวณความแตกต่างใช้เวลา <1 มิลลิวินาทีสำหรับรายการสูงสุด 1,000 รายการและรับประกันแอนิเมชันที่ราบรื่น

การเก็บออบเจ็กต์หนักในอะแดปเตอร์

อะแดปเตอร์มีอายุยืนเท่ากับ RecyclerView/UITableView การเก็บ Bitmap หรืออาร์เรย์ขนาดใหญ่ ในอะแดปเตอร์นำไปสู่การรั่วไหลของหน่วยความจำเมื่อเปลี่ยนการกำหนดค่า (หมุนหน้าจอ เปลี่ยนธีม) วิธีแก้: อะแดปเตอร์ควรเก็บเฉพาะโมเดลข้อมูลน้ำหนักเบา (data class / struct) และโหลดทรัพยากรหนักผ่าน ViewHolder เมื่อจำเป็น

คำถามที่พบบ่อย

ความแตกต่างระหว่าง Adapter และ Proxy คืออะไร?

Adapter แปลงอินเทอร์เฟซ Adaptee เป็นอินเทอร์เฟซ Target — ไคลเอ็นต์ได้รับวิธีการโต้ตอบใหม่ Proxy ให้อินเทอร์เฟซเดียวกันกับออบเจ็กต์ดั้งเดิม แต่เพิ่มการควบคุมการเข้าถึง แคช หรือการเริ่มต้นแบบขี้เกียจ Proxy ไม่เปลี่ยนอินเทอร์เฟซ Adapter เปลี่ยน

ทำไม RecyclerView.Adapter จึงเป็นตัวอย่างของรูปแบบ Adapter?

RecyclerView.Adapter แปลงข้อมูล (เช่น รายการ User) เป็น ViewHolder ที่ RecyclerView สามารถแสดงได้ RecyclerView คาดหวัง ViewHolder ข้อมูลอยู่ในรูปแบบ List<User> — Adapter ปรับให้เข้าคู่กัน นอกจากนี้ Adapter จัดการวงจรชีวิตของ ViewHolder ผ่านพูลรีไซเคิล เพิ่มประสิทธิภาพของรายการ

ความแตกต่างระหว่าง Object Adapter และ Class Adapter คืออะไร?

Object Adapter ใช้การประกอบ: เก็บการอ้างอิงถึง Adaptee และมอบหมายการเรียก Class Adapter ใช้การสืบทอดหลายคลาส: สืบทอดจาก Adaptee และใช้งาน Target ใน Java, Kotlin และ Swift มีเฉพาะ Object Adapter เท่านั้นเนื่องจากขาดการสืบทอดหลายคลาส Class Adapter เป็นไปได้เฉพาะใน C++

เมื่อใดควรใช้ Adapter แทน Facade?

Adapter ใช้เมื่อคุณต้องการทำให้สองคลาสที่มีอยู่ทำงานร่วมกันโดยการแปลงอินเทอร์เฟซของหนึ่งในนั้น Facade ใช้เมื่อคุณต้องการทำให้การโต้ตอบกับระบบย่อยที่ซับซ้อนง่ายขึ้นโดยการให้อินเทอร์เฟซที่เรียบง่าย Adapter สำหรับความเข้ากันได้ Facade สำหรับการทำให้ง่ายขึ้น

สามารถใช้งาน Adapter โดยไม่มีอินเทอร์เฟซได้หรือไม่?

ได้ โดยใช้ คลอเชอร์ แทนอินเทอร์เฟซที่มีหนึ่งเมธอด สามารถส่งฟังก์ชันได้ ตัวอย่างเช่น ใน Swift: let adapter = { (data: Data) -> [CellConfig] in /* การแปลง */ } วิธีการนี้เรียกว่าอะแดปเตอร์เชิงฟังก์ชัน อย่างไรก็ตาม สำหรับ 2+ เมธอด โปรโตคอล/อินเทอร์เฟซยังคงเป็นที่นิยม — ทำให้สัญญาชัดเจน

สรุป

  • Adapter คือรูปแบบเชิงโครงสร้างที่แปลงอินเทอร์เฟซ Adaptee ที่เข้ากันไม่ได้เป็น Target ผ่านคลาสตัวกลาง
  • RecyclerView.Adapter (Android) และ UITableViewDataSource (iOS) เป็นตัวอย่างหลักของรูปแบบในการพัฒนาแอปพลิเคชันมือถือ
  • Object Adapter ใช้การประกอบ Class Adapter (เฉพาะ C++) ใช้การสืบทอดหลายคลาส
  • ListAdapter + DiffUtil (Android) และ DiffableDataSource (iOS) เป็นวิวัฒนาการที่ทันสมัยพร้อมการคำนวณความแตกต่างอัตโนมัติและแอนิเมชัน
  • การดำเนินการหนักใน onBindViewHolder การละเว้น DiffUtil และการเก็บทรัพยากรในอะแดปเตอร์เป็นข้อผิดพลาดทั่วไป
  • Adapter ใช้สำหรับรวมโค้ดเก่า ปรับรูปแบบ และรวมการใช้งานที่แตกต่างกัน
  • คำแนะนำ: ใช้ ListAdapter/DiffableDataSource แทนการจัดการอัปเดตรายการด้วยตนเอง

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม