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)وراثت (extends Adaptee)
انعطاف‌پذیریبیشتر — Adaptee را می‌توان در runtime تغییر دادکمتر — Adaptee در زمان کامپایل ثابت است
وابستگیضعیف (از طریق رابط)قوی (از طریق وراثت)
تطبیق زیرکلاس‌هابله — Adaptee می‌تواند هر زیرکلاسی باشدخیر — فقط سوپرکلاس مشخص
دسترسیJava, Kotlin, Swift, C++, C#فقط زبان‌های با وراثت چندگانه (C++)

در توسعه موبایل فقط Object Adapter استفاده می‌شود. RecyclerView.Adapter ارجاعاتی به داده‌ها و LayoutInflater دارد، UITableViewDataSource آرایه‌ای از عناصر دارد. ترکیب کد را نسبت به وراثت انعطاف‌پذیرتر و قابل تست‌تر می‌کند.

نمونه‌های Adapter در پروژه‌های واقعی

Adapter نه تنها برای لیست‌ها استفاده می‌شود. سه سناریو از تمرین توسعه موبایل را بررسی می‌کنیم که در آن الگو مشکل یکپارچه‌سازی مؤلفه‌های ناسازگار را حل می‌کند.

سناریوAdapteeTargetAdapter
یکپارچه‌سازی کتابخانه احراز هویت قدیمیLegacyAuthLib (callback-based)AuthService (suspend)LegacyAuthAdapter
تطبیق JSON با مدل جدیدGsonParserJsonParser (Kotlinx Serialization)GsonAdapter
رابط یکپارچه برای سرویس‌های pushFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

هر Adapter منطق تبدیل را کپسوله می‌کند: GsonAdapter رشته JSON را از طریق @SerializedName به شیء تبدیل می‌کند، PushServiceAdapter ثبت توکن را برای پلتفرم‌های مختلف انتزاع می‌کند. مشتری (منطق کسب‌وکار) با یک رابط یکپارچه کار می‌کند و به پیاده‌سازی خاص وابسته نیست.

اشتباهات رایج در پیاده‌سازی Adapter

اشتباهات در پیاده‌سازی Adapter منجر به کندی لیست‌ها، نشت حافظه و باگ‌های به‌روزرسانی داده می‌شود. سه مشکل رایج را بررسی می‌کنیم.

عملیات سنگین در onBindViewHolder

onBindViewHolder برای هر سلول هنگام اسکرول فراخوانی می‌شود. ایجاد اشیاء، تجزیه JSON یا پرس‌وجوی پایگاه داده در این متد منجر به لرزش لیست (frame drops) می‌شود. راه‌حل: تمام محاسبات سنگین را از قبل انجام دهید، داده‌های آماده را به آداپتر منتقل کنید. برای تصاویر از کتابخانه‌های ذخیره‌سازی استفاده کنید — Glide، Coil، Kingfisher.

نادیده گرفتن DiffUtil / DiffableDataSource

فراخوانی notifyDataSetChanged() در هر به‌روزرسانی کل لیست را بازنویسی می‌کند و باعث سوسو زدن و از دست رفتن فوکس در ورودی‌ها می‌شود. راه‌حل: از ListAdapter با DiffUtil در Android یا DiffableDataSource در iOS استفاده کنید. محاسبه تفاوت برای لیست‌های تا 1000 عنصر <1 ms طول می‌کشد و انیمیشن‌های روان را تضمین می‌کند.

ذخیره اشیاء سنگین در آداپتر

آداپتر به اندازه 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 را بدون رابط پیاده‌سازی کرد؟

بله، با استفاده از بستارها (closures). به جای رابط با یک متد می‌توان یک تابع ارسال کرد. مثلاً در Swift: let adapter = { (data: Data) -> [CellConfig] in /* تبدیل */ }. این رویکرد functional adapter نامیده می‌شود. اما برای 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 از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید