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 एक ही समस्या को विभिन्न API लेकिन समान तर्क — डिफ़ गणना + स्वचालित एनिमेशन — से हल करते हैं।
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 |
|---|---|---|---|
| पुरानी auth लाइब्रेरी का एकीकरण | LegacyAuthLib (कॉलबैक-आधारित) | AuthService (suspend) | LegacyAuthAdapter |
| नए मॉडल के लिए JSON अनुकूलन | GsonParser | JsonParser (Kotlinx Serialization) | GsonAdapter |
| पुश सेवाओं के लिए एकीकृत इंटरफ़ेस | FCM, APNs, Huawei Push | PushService token register | PushServiceAdapter |
प्रत्येक Adapter रूपांतरण तर्क को Encapsulate करता है: GsonAdapter @SerializedName के माध्यम से JSON स्ट्रिंग को ऑब्जेक्ट में अनुवाद करता है, PushServiceAdapter विभिन्न प्लेटफ़ॉर्म के लिए टोकन पंजीकरण को अमूर्त करता है। क्लाइंट (व्यावसायिक तर्क) एकीकृत इंटरफ़ेस के साथ काम करता है और विशिष्ट कार्यान्वयन पर निर्भर नहीं करता।
Adapter कार्यान्वयन में गलतियाँ सूची की धीमी गति, मेमोरी लीक और डेटा अपडेट में बग का कारण बनती हैं। आइए तीन सामान्य समस्याओं पर विचार करें।
onBindViewHolder स्क्रॉल करते समय प्रत्येक सेल के लिए कॉल किया जाता है। इस विधि में ऑब्जेक्ट बनाना, JSON पार्स करना या डेटाबेस कॉल करना फ्रेम ड्रॉप का कारण बनता है। समाधान: सभी भारी गणनाएँ पहले से करें, एडॉप्टर को तैयार डेटा पास करें। छवियों के लिए कैशिंग लाइब्रेरी का उपयोग करें — Glide, Coil, Kingfisher।
प्रत्येक अपडेट पर notifyDataSetChanged() कॉल करना पूरी सूची को पुनर्चित्रित करता है, जिससे झिलमिलाहट और इनपुट पर फ़ोकस खोना होता है। समाधान: Android में DiffUtil के साथ ListAdapter या iOS में DiffableDataSource का उपयोग करें। डिफ़ गणना में 1000 आइटम तक की सूचियों के लिए <1 मी.से. लगता है और चिकनी एनिमेशन सुनिश्चित करता है।
एडॉप्टर तब तक जीवित रहता है जब तक 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 सरलीकरण के लिए।
हाँ, क्लोज़र्स का उपयोग करके। एक विधि वाले इंटरफ़ेस के बजाय, एक फ़ंक्शन पास किया जा सकता है। उदाहरण के लिए, Swift में: let adapter = { (data: Data) -> [CellConfig] in /* रूपांतरण */ }। इस दृष्टिकोण को कार्यात्मक एडॉप्टर कहा जाता है। हालांकि, 2+ विधियों के लिए, प्रोटोकॉल/इंटरफ़ेस बेहतर रहता है — यह अनुबंध को स्पष्ट बनाता है।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें