Adapter ایک ساختی ڈیزائن پیٹرن ہے جو ایک کلاس کے انٹرفیس کو دوسرے انٹرفیس میں تبدیل کرتا ہے جس کی کلائنٹ توقع کرتا ہے۔ موبائل ڈیولپمنٹ میں، Adapter سب سے زیادہ Android میں RecyclerView.Adapter اور iOS میں UITableViewDataSource کے طور پر استعمال ہوتا ہے۔ Google I/O (2024) کے مطابق، RecyclerView 95% Android ایپس میں فہرستوں کو ظاہر کرنے کے لیے استعمال ہوتا ہے، اور ہر ایک کو اپنے Adapter کے نفاذ کی ضرورت ہوتی ہے۔
اہم نکات
Adapter ایک ساختی پیٹرن ہے جو انٹرفیس کی عدم مطابقت کے مسئلے کو حل کرتا ہے۔ یہ ایک آبجیکٹ (Adaptee) کو ایک کلاس (Adapter) میں لپیٹتا ہے جس میں کلائنٹ کا متوقع انٹرفیس (Target) ہوتا ہے۔ کلائنٹ Target کے ساتھ کام کرتا ہے اور Adaptee کے وجود سے آگاہ نہیں ہوتا۔ یہ Wrapper پیٹرن کی ایک قسم ہے۔
// غیر مطابقت پذیر انٹرفیس والی موجودہ کلاس
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 پرانی API کے لیے login(credentials) کال کو loginWithToken(token) میں تبدیل کرتا ہے۔ کلائنٹ (ViewModel) AuthService انٹرفیس کے ذریعے کام کرتا ہے اور نہیں جانتا کہ اس کے نیچے تازہ ترین Firebase Auth ہے یا دہائیوں پرانی لیگیسی API۔ یہ کلائنٹ کوڈ کو تبدیل کیے بغیر نفاذ کو تبدیل کرنے کی اجازت دیتا ہے۔
RecyclerView.Adapter Android میں Adapter پیٹرن کا سب سے عام نفاذ ہے۔ یہ ڈیٹا (اشیاء کی فہرست) کو ViewHolders میں تبدیل کرتا ہے جو RecyclerView اسکرین پر ظاہر کرتا ہے۔ ListAdapter (Android Architecture Components) کے متعارف ہونے کے ساتھ، پیٹرن کو تبدیلیوں کو متحرک کرنے کے لیے بلٹ ان فرق حساب کتاب کی مدد ملی۔
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 موثر فہرست دوبارہ ڈرائنگ کے لیے DiffUtil کے ساتھ ListAdapter استعمال کرتا ہے۔ جب ڈیٹا اپ ڈیٹ ہوتا ہے، DiffCallback پرانی اور نئی فہرست کے درمیان فرق کا حساب لگاتا ہے، اور RecyclerView صرف تبدیل شدہ آئٹمز کو متحرک کرتا ہے — پوری فہرست کو دوبارہ ڈرائنگ کیے بغیر۔ یہ پرانی ListView کے مقابلے میں ایک اہم فائدہ ہے، جس کے لیے دستی notifyDataSetChanged() کی ضرورت تھی۔
UITableViewDataSource ایک ساتھ Adapter پیٹرن اور DataSource پیٹرن دونوں ہے۔ یہ ماڈل ڈیٹا کو ٹیبل سیلز میں تبدیل کرتا ہے۔ iOS 13 سے، Apple نے DiffableDataSource متعارف کرایا — ایک جدید متبادل، جو Android میں ListAdapter کی طرح، خود بخود تبدیلیوں کا حساب لگاتا ہے اور اپ ڈیٹس کو متحرک کرتا ہے۔
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 کے ساتھ ڈیٹا اپ ڈیٹ ہونے پر خود بخود تبدیلیوں کو متحرک کرتا ہے: آئٹمز ہموار اینیمیشن کے ساتھ ظاہر، غائب یا منتقل ہوتے ہیں۔ iOS کا DiffableDataSource اور Android کا ListAdapter مختلف APIs سے ایک ہی مسئلہ حل کرتے ہیں لیکن ایک ہی منطق سے — فرق حساب + خودکار اینیمیشن۔
Object Adapter کمپوزیشن استعمال کرتا ہے: اڈاپٹر میں Adaptee آبجیکٹ کا حوالہ ہوتا ہے اور اسے کالز تفویض کرتا ہے۔ Class Adapter وراثت استعمال کرتا ہے: اڈاپٹر Adaptee سے وراثت پاتا ہے اور بیک وقت ہدف انٹرفیس کو لاگو کرتا ہے۔ Java، Swift اور Kotlin میں، متعدد وراثت کی کمی کی وجہ سے Class Adapter ناممکن ہے — صرف Object Adapter رہ جاتا ہے۔
| خصوصیت | Object Adapter | Class Adapter |
|---|---|---|
| میکانزم | کمپوزیشن (Adaptee پر مشتمل) | وراثت (Adaptee کو بڑھاتا ہے) |
| لچک | زیادہ — Adaptee کو رن ٹائم پر تبدیل کیا جا سکتا ہے | کم — Adaptee کمپائل ٹائم پر مقرر |
| وابستگی | کمزور (انٹرفیس کے ذریعے) | مضبوط (وراثت کے ذریعے) |
| ذیلی کلاس موافقت | ہاں — Adaptee کوئی بھی ذیلی کلاس ہو سکتی ہے | نہیں — صرف مخصوص سپرکلاس |
| دستیابی | Java, Kotlin, Swift, C++, C# | صرف متعدد وراثت والی زبانیں (C++) |
موبائل ڈیولپمنٹ میں، صرف Object Adapter استعمال ہوتا ہے۔ RecyclerView.Adapter میں ڈیٹا اور LayoutInflater کے حوالے ہوتے ہیں، UITableViewDataSource میں آئٹمز کی ایک صف ہوتی ہے۔ کمپوزیشن وراثت کے مقابلے میں کوڈ کو زیادہ لچکدار اور قابل جانچ بناتی ہے۔
Adapter صرف فہرستوں کے لیے استعمال نہیں ہوتا۔ آئیے موبائل ڈیولپمنٹ پریکٹس سے تین منظرناموں پر غور کریں جہاں پیٹرن غیر مطابقت پذیر اجزاء کو ضم کرنے کا مسئلہ حل کرتا ہے۔
| منظر | Adaptee | Target | Adapter |
|---|---|---|---|
| پرانی تصدیقی لائبریری کا انضمام | LegacyAuthLib (کال بیک پر مبنی) | AuthService (suspend) | LegacyAuthAdapter |
| نئے ماڈل کے لیے JSON کی موافقت | GsonParser | JsonParser (Kotlinx Serialization) | GsonAdapter |
| پش سروسز کے لیے متحد انٹرفیس | FCM, APNs, Huawei Push | PushService token register | PushServiceAdapter |
ہر Adapter تبدیلی کی منطق کو انکیپسلیٹ کرتا ہے: GsonAdapter @SerializedName کے ذریعے JSON سٹرنگ کو آبجیکٹ میں ترجمہ کرتا ہے، PushServiceAdapter مختلف پلیٹ فارمز کے لیے ٹوکن رجسٹریشن کو خلاصہ کرتا ہے۔ کلائنٹ (کاروباری منطق) متحد انٹرفیس کے ساتھ کام کرتا ہے اور مخصوص نفاذ پر منحصر نہیں ہوتا۔
Adapter کے نفاذ میں غلطیاں فہرست کی سستی، میموری لیک اور ڈیٹا اپ ڈیٹ میں خرابیوں کا باعث بنتی ہیں۔ آئیے تین عام مسائل پر غور کریں۔
onBindViewHolder اسکرولنگ کے دوران ہر سیل کے لیے کہا جاتا ہے۔ اس طریقہ میں آبجیکٹ بنانا، JSON تجزیہ کرنا یا ڈیٹابیس کو کال کرنا فریم ڈراپ کا سبب بنتا ہے۔ حل: تمام بھاری حسابات پہلے سے کریں، اڈاپٹر کو تیار ڈیٹا پاس کریں۔ تصاویر کے لیے کیشنگ لائبریریاں استعمال کریں — Glide، Coil، Kingfisher۔
ہر اپ ڈیٹ پر notifyDataSetChanged() کو کال کرنا پوری فہرست کو دوبارہ کھینچتا ہے، جس سے جھلملاہٹ اور ان پٹ پر فوکس کا نقصان ہوتا ہے۔ حل: Android میں DiffUtil کے ساتھ ListAdapter یا iOS میں DiffableDataSource استعمال کریں۔ فرق حساب میں 1000 آئٹمز تک کی فہرستوں کے لیے <1 ms لگتا ہے اور ہموار اینیمیشن کو یقینی بناتا ہے۔
اڈاپٹر اس وقت تک زندہ رہتا ہے جب تک RecyclerView/UITableView۔ اڈاپٹر میں Bitmap یا بڑی صفیں ذخیرہ کرنا کنفیگریشن تبدیلیوں (اسکرین گھومنے، تھیم تبدیل کرنے) پر میموری لیک کا سبب بنتا ہے۔ حل: اڈاپٹر کو صرف ہلکے ڈیٹا ماڈل (data class / struct) ذخیرہ کرنے چاہئیں، اور بھاری وسائل کو ضرورت پر ViewHolder کے ذریعے لوڈ کرنا چاہیے۔
اکثر پوچھے گئے سوالات
Adapter Adaptee انٹرفیس کو Target انٹرفیس میں تبدیل کرتا ہے — کلائنٹ کو تعامل کا ایک نیا طریقہ ملتا ہے۔ Proxy اصل آبجیکٹ جیسا ہی انٹرفیس فراہم کرتا ہے، لیکن رسائی کنٹرول، کیشنگ یا سست ابتدا کا اضافہ کرتا ہے۔ Proxy انٹرفیس نہیں بدلتا، Adapter بدلتا ہے۔
RecyclerView.Adapter ڈیٹا (مثلاً، User کی فہرست) کو ViewHolder میں تبدیل کرتا ہے جسے RecyclerView ظاہر کر سکتا ہے۔ RecyclerView ViewHolder کی توقع کرتا ہے، ڈیٹا List<User> فارمیٹ میں ہے — Adapter ایک کو دوسرے کے مطابق ڈھالتا ہے۔ مزید برآں، Adapter ری سائیکل پول کے ذریعے ViewHolder لائف سائیکل کا انتظام کرتا ہے، فہرست کی کارکردگی کو بہتر بناتا ہے۔
Object Adapter کمپوزیشن استعمال کرتا ہے: اس میں Adaptee کا حوالہ ہوتا ہے اور اسے کالز تفویض کرتا ہے۔ Class Adapter متعدد وراثت استعمال کرتا ہے: یہ Adaptee سے وراثت پاتا ہے اور Target کو لاگو کرتا ہے۔ Java، Kotlin اور Swift میں، متعدد کلاس وراثت کی کمی کی وجہ سے صرف Object Adapter دستیاب ہے۔ Class Adapter صرف C++ میں ممکن ہے۔
Adapter اس وقت استعمال ہوتا ہے جب آپ کو دو موجودہ کلاسوں کو ایک ساتھ کام کرنے کی ضرورت ہو، ان میں سے ایک کے انٹرفیس کو تبدیل کرکے۔ Facade اس وقت استعمال ہوتا ہے جب آپ کو ایک سادہ انٹرفیس فراہم کرکے ایک پیچیدہ ذیلی نظام کے ساتھ تعامل کو آسان بنانے کی ضرورت ہو۔ Adapter مطابقت کے لیے، Facade آسان بنانے کے لیے۔
ہاں، closure استعمال کرکے۔ ایک طریقہ والے انٹرفیس کے بجائے، ایک فنکشن پاس کیا جا سکتا ہے۔ مثال کے طور پر، Swift میں: let adapter = { (data: Data) -> [CellConfig] in /* تبدیلی */ }۔ اس نقطہ نظر کو فنکشنل اڈاپٹر کہا جاتا ہے۔ تاہم، 2+ طریقوں کے لیے، پروٹوکول/انٹرفیس ترجیحی رہتا ہے — یہ معاہدے کو واضح کرتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں