MVP (Model-View-Presenter) — एक आर्किटेक्चरल पैटर्न है जिसमें Presenter, ViewContract इंटरफ़ेस के माध्यम से Model और View के बीच मध्यस्थ के रूप में कार्य करता है। MVC के विपरीत, जहाँ Controller सीधे UIKit के माध्यम से View को नियंत्रित करता है, Presenter फ्रेमवर्क पर निर्भर नहीं होता — यह एब्स्ट्रैक्शन के माध्यम से काम करता है, जो इसे Android SDK या UIKit के बिना परीक्षण योग्य बनाता है। Jetpack के आगमन से पहले Android डेवलपमेंट में MVP का व्यापक रूप से उपयोग किया जाता है और यह लीगेसी प्रोजेक्ट्स के लिए प्रासंगिक बना हुआ है। अधिक जानकारी के लिए — मार्टिन फाउलर का लेख देखें।
मुख्य बातें
MVP (Model-View-Presenter) — एक आर्किटेक्चरल पैटर्न है जिसे मार्टिन फाउलर ने 2000 के दशक की शुरुआत में उपयोगकर्ता इंटरफ़ेस की परीक्षण क्षमता में सुधार के लिए MVC के विकास के रूप में प्रस्तावित किया था। Model डेटा और व्यावसायिक लॉजिक का प्रबंधन करता है, View प्रदर्शन और उपयोगकर्ता इनपुट प्रसंस्करण के लिए जिम्मेदार है, Presenter केंद्रीय घटक है जो View से ईवेंट प्राप्त करता है, Model से डेटा प्राप्त करता है और प्रदर्शन के लिए स्थिति बनाता है।
MVP और MVC के बीच मुख्य अंतर — Presenter के पास View का सीधा संदर्भ नहीं होता। इसके बजाय, Presenter ViewContract इंटरफ़ेस के माध्यम से View के साथ इंटरैक्ट करता है। View इस इंटरफ़ेस को लागू करता है और स्वयं को Presenter को भेजता है। यह UIKit (iOS) या Android Framework पर निर्भरता को तोड़ता है — Presenter को ViewContract के mock कार्यान्वयन के साथ अलगाव में परीक्षण किया जा सकता है। MVC में UIViewController कंट्रोलर सीधे UILabel को अपडेट करता है, MVP में Presenter view.showName(name) को कॉल करता है, और View तय करता है कि कैसे प्रदर्शित करना है।
| घटक | जिम्मेदारी | परीक्षण क्षमता |
|---|---|---|
| Model | डेटा, व्यावसायिक लॉजिक, नेटवर्क कॉल | यूनिट टेस्ट (UI से स्वतंत्र) |
| View | UI प्रदर्शन, Presenter को ईवेंट भेजना | इंटरफ़ेस के माध्यम से mock कार्यान्वयन |
| Presenter | व्यावसायिक लॉजिक, स्थिति प्रबंधन, नेविगेशन | यूनिट टेस्ट (ViewContract mock के माध्यम से) |
एकल जिम्मेदारी सिद्धांत MVP में MVC की तुलना में अधिक सख्ती से पालन किया जाता है: View केवल रेंडरिंग के लिए जिम्मेदार है, Model डेटा के लिए, Presenter लॉजिक और समन्वय के लिए। वास्तविक प्रोजेक्ट्स में Presenter स्क्रीन के 40–60% कोड को घेरता है, View — 20–30%, Model — 20–30%. यह वितरण Android एमुलेटर या iOS सिम्युलेटर को लॉन्च किए बिना मुख्य व्यावसायिक लॉजिक का परीक्षण करने की अनुमति देता है।
Android में MVP Activity या Fragment को View के रूप में उपयोग करता है, जो ViewContract को लागू करता है — डेटा प्रदर्शन विधियों वाला एक इंटरफ़ेस। Presenter Activity में बनाया जाता है, View को अपने साथ जोड़ता है और डेटा लोडिंग का प्रबंधन करता है। जब स्क्रीन घूमती है, Activity पुनः बनाई जाती है — Presenter को retain-fragment या बाहरी स्टोरेज के माध्यम से संरक्षित किया जा सकता है, जो शुद्ध MVC की स्थिति हानि समस्या को हल करता है।
// ViewContract — Presenter को View से जोड़ने का इंटरफ़ेस
interface UserView {
fun showLoading()
fun hideLoading()
fun showUser(user: User)
fun showError(message: String)
}
// Presenter — परीक्षण योग्य लॉजिक परत
class UserPresenter(
private val repository: UserRepository
) {
private var view: UserView? = null
fun attachView(view: UserView) {
this.view = view
}
fun detachView() {
view = null
}
fun loadUser(userId: Int) {
view?.showLoading()
repository.getUser(userId) { result ->
view?.hideLoading()
result.onSuccess { user ->
view?.showUser(user)
}.onFailure { e ->
view?.showError(e.message ?: "Unknown error")
}
}
}
}
// View (Activity) इंटरफ़ेस को लागू करता है
class UserActivity : AppCompatActivity(), UserView {
private val presenter = UserPresenter(UserRepository())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
presenter.attachView(this)
presenter.loadUser(42)
}
override fun onDestroy() {
presenter.detachView()
super.onDestroy()
}
override fun showUser(user: User) { /* UI अपडेट करें */ }
override fun showLoading() { /* ProgressBar दिखाएं */ }
override fun hideLoading() { /* ProgressBar छुपाएं */ }
override fun showError(message: String) { /* Snackbar दिखाएं */ }
}
जीवनचक्र प्रबंधन — Android पर MVP की एक मुख्य समस्या है। स्क्रीन घुमाने पर Activity नष्ट हो जाती है, और presenter.attachView() को onCreate() में फिर से कॉल किया जाता है। यदि डेटा लोडिंग एसिंक्रोनस है (RxJava, कोरूटीन), तो पूरा होने तक View डिटैच हो सकती है। समाधान — detachView() में सब्सक्रिप्शन रद्द करना या Support Library से Loader का उपयोग करना (Jetpack के बिना प्रोजेक्ट्स के लिए)। IT Sectr में हमने वाणिज्यिक प्रोजेक्ट्स में वर्षों तक MVP + RxJava संयोजन का उपयोग किया — पैटर्न स्थिर है लेकिन सब्सक्रिप्शन प्रबंधन में अनुशासन की आवश्यकता होती है।
Retain फ़्रैगमेंट — स्क्रीन घुमाने पर Presenter को संरक्षित करने की एक तंत्र। बिना UI वाला फ़्रैगमेंट (setRetainInstance(true)) Activity से अधिक जीवित रहता है और Presenter का संदर्भ रखता है। जब Activity पुनः बनाई जाती है, फ़्रैगमेंट उसी Presenter को नई Activity को भेजता है। Retain फ़्रैगमेंट AndroidX से deprecated हैं, लेकिन उनका pre-Jetpack एनालॉग (Fragment.setRetainInstance) अभी भी लीगेसी प्रोजेक्ट्स में काम करता है। आधुनिक डेवलपमेंट में Google retain फ़्रैगमेंट के बजाय ViewModel की सिफारिश करता है।
iOS में MVP View प्रोटोकॉल के माध्यम से बनाया जाता है। UIViewController प्रोटोकॉल को लागू करता है, Presenter UIKit को आयात नहीं करता और शुद्ध रूप से परीक्षण योग्य है। Apple MVC के विपरीत, जहाँ UIViewController स्वयं लॉजिक और सीधे IBOutlet कनेक्शन शामिल करता है, Presenter स्थिति का प्रबंधन करता है और प्रोटोकॉल विधियों के माध्यम से View को निर्देश देता है। View निर्णय नहीं लेती — वह Presenter के आदेशों को निष्पादित करती है: showUser, showLoading, navigateToProfile.
import Foundation
// View Protocol — Presenter के लिए एब्स्ट्रैक्शन
protocol UserViewProtocol: AnyObject {
func showLoading()
func hideLoading()
func display(user: User)
func displayError(message: String)
}
// Presenter — शुद्ध लॉजिक, बिना UIKit के
final class UserPresenter {
private weak var view: UserViewProtocol?
private let service: UserServiceProtocol
init(service: UserServiceProtocol) {
self.service = service
}
func attach(view: UserViewProtocol) {
self.view = view
}
func detach() {
view = nil
}
func loadUser(id: Int) {
view?.showLoading()
service.fetchUser(id: id) { [weak self] result in
guard let self else { return }
self.view?.hideLoading()
switch result {
case .success(let user):
self.view?.display(user: user)
case .failure(let error):
self.view?.displayError(message: error.localizedDescription)
}
}
}
}
// View (UIViewController) प्रोटोकॉल को लागू करता है
final class UserViewController: UIViewController, UserViewProtocol {
private let presenter = UserPresenter(service: UserService())
override func viewDidLoad() {
super.viewDidLoad()
presenter.attach(view: self)
presenter.loadUser(id: 42)
}
func display(user: User) {
nameLabel.text = user.name
}
// ... प्रोटोकॉल की शेष विधियाँ
}
Weak reference View पर — iOS MVP में अनिवार्य शर्त है। UIViewController नष्ट हो सकता है (navigation stack से pop), और Presenter में इसका क्लोजर retain cycle बनाएगा। कमजोर संदर्भ (weak var) गारंटी देता है कि View स्क्रीन छोड़ने पर मुक्त हो जाती है, Presenter में एसिंक्रोनस संचालन की परवाह किए बिना। Android में इसी तरह की समस्या detachView() के माध्यम से हल की जाती है — onDestroy() में कॉल करने से View का संदर्भ शून्य हो जाता है।
Passive View vs Supervising Controller — मार्टिन फाउलर के MVP के दो वेरिएंट। Passive View: View में कोई लॉजिक नहीं है, Presenter पूरी तरह से स्थिति का प्रबंधन करता है। Supervising Controller: View स्वयं सरल डेटा बाइंडिंग करता है (उदाहरण के लिए, data binding के माध्यम से), Presenter केवल जटिल परिदृश्यों में हस्तक्षेप करता है। मोबाइल डेवलपमेंट में Passive View का अधिक उपयोग किया जाता है — यह अधिकतम परीक्षण क्षमता और स्क्रीन स्थिति की पूर्वानुमेयता प्रदान करता है।
मुख्य अंतर MVP और MVC के बीच View से संवाद करने का तरीका है। MVC में Controller का View (UIViewController.IBOutlets, Activity.findViewById) पर सीधा संदर्भ होता है। MVP में Presenter ViewContract इंटरफ़ेस के माध्यम से View के साथ इंटरैक्ट करता है। यह अंतर मौलिक रूप से परीक्षण क्षमता को बदल देता है: ViewContract को लागू करने वाली mock वस्तु ऐप, एमुलेटर या UI फ्रेमवर्क को लॉन्च किए बिना Presenter के लॉजिक का परीक्षण करने की अनुमति देती है।
| मानदंड | MVC | MVP |
|---|---|---|
| View से संबंध | सीधा (Controller → View) | इंटरफ़ेस के माध्यम से (Presenter → ViewContract) |
| लॉजिक परीक्षण | UIKit/Android Framework की आवश्यकता | प्लेटफ़ॉर्म निर्भरता के बिना यूनिट टेस्ट |
| जीवनचक्र | Controller स्क्रीन के साथ रहता है | Presenter अधिक समय तक रह सकता है (retain) |
| जटिलता | न्यूनतम | +1 इंटरफ़ेस प्रति स्क्रीन |
| Massive Controller | सामान्य समस्या | लॉजिक Presenter में, View पतली |
Presenter के यूनिट टेस्ट का उदाहरण Kotlin में: एक mock UserView बनाया जाता है, Presenter को भेजा जाता है, loadUser कॉल किया जाता है, जाँच की जाती है कि showUser सही डेटा के साथ कॉल हुआ। टेस्ट मिलीसेकंड में निष्पादित होता है, किसी एमुलेटर की आवश्यकता नहीं। iOS पर समान रूप से — OCMock या प्रोटोकॉल स्टब UserViewProtocol विधि कॉल की जाँच करता है। IT Sectr के MVP वाले प्रोजेक्ट्स में, व्यावसायिक लॉजिक का यूनिट टेस्ट कवरेज 85–90% तक पहुँच गया, जो समान MVC प्रोजेक्ट्स की तुलना में 2–3 गुना अधिक है।
MVP कब MVC से बेहतर है — स्थिरता की सख्त आवश्यकताओं वाले प्रोजेक्ट्स में: बैंकिंग ऐप्स, चिकित्सा प्रणालियाँ, भुगतान टर्मिनल। इन क्षेत्रों में त्रुटि की लागत अधिक होती है, और यूनिट टेस्ट महत्वपूर्ण होते हैं। पोस्ट-MVP प्रोजेक्ट्स में (जब उत्पाद पहले से बाजार में है लेकिन कोडबेस लीगेसी है), MVP पूर्ण आर्किटेक्चरल पुनर्लेखन के बिना Massive View Controller से लॉजिक को परीक्षण योग्य परत में धीरे-धीरे निकालने की अनुमति देता है।
MVP के मुख्य नुकसान — इंटरफ़ेस की संख्या में वृद्धि और मैनुअल सब्सक्रिप्शन प्रबंधन। प्रत्येक स्क्रीन के लिए कम से कम एक ViewContract + Presenter की आवश्यकता होती है, 50 स्क्रीन के लिए — 50 इंटरफ़ेस और 50 Presenter क्लास। MVVM में, ViewModel Presenter की जगह लेता है और रिएक्टिव मैकेनिज़्म (LiveData, StateFlow, ObservableObject) का उपयोग करता है, जो मैनुअल attach/detach और ViewContract इंटरफ़ेस की आवश्यकता को समाप्त करता है।
RxJava और MVP — Android 2015–2019 में एक लोकप्रिय संयोजन। Presenter Repository से Observable पर सब्सक्राइब करता है, ViewContract के माध्यम से परिणाम प्रदर्शित करता है। समस्या: disposable को detachView() में स्पष्ट रूप से रद्द करना आवश्यक है, अन्यथा सब्सक्रिप्शन लीक डिटैच हुई View को अपडेट करते समय क्रैश का कारण बनेगा। RxLifecycle और AutoDispose लाइब्रेरीज़ ने आंशिक रूप से अनसब्सक्राइबिंग को स्वचालित किया लेकिन निर्भरताएँ जोड़ीं। IT Sectr में हमने 2020 में MVP+RxJava से MVVM+Flow पर स्विच किया — ViewContract के समाप्त होने के कारण कोड 25–30% छोटा हो गया।
MVP से MVVM में संक्रमण — एक क्रमिक प्रक्रिया। 1) Presenter में ViewContract को LiveData/StateFlow से बदलें। 2) attach/detach विधियाँ हटाएँ — सब्सक्रिप्शन observe() के माध्यम से होता है। 3) Presenter का नाम बदलकर ViewModel करें। 4) ViewModelFactory के लिए DI (Hilt/Koin) को एकीकृत करें। एक स्क्रीन का माइग्रेशन 2–4 घंटे लगता है, पूरे कोडबेस का — 50–100 स्क्रीन वाले प्रोजेक्ट के लिए 2–4 सप्ताह। माइग्रेशन के बाद, ViewContract इंटरफ़ेस हटा दिए जाते हैं, कोड सिकुड़ जाता है, टेस्ट बने रहते हैं।
आधुनिक डेवलपमेंट में MVP — पैटर्न जीवित है लेकिन MVVM और MVI से पीछे है। Google आधिकारिक तौर पर नए प्रोजेक्ट्स के लिए Jetpack के साथ MVVM की सिफारिश करता है। Apple — SwiftUI के साथ MVVM। हालाँकि, लीगेसी कोड के साथ काम करने के लिए MVP का ज्ञान अनिवार्य है: Google Play पर सैकड़ों Android ऐप्स अभी भी MVP पर चलते हैं, जिनमें बड़े बैंकों, खुदरा विक्रेताओं और परिवहन कंपनियों के ऐप्स शामिल हैं। MVP को समझना MVI और Clean Architecture में महारत हासिल करने का आधार है, क्योंकि Presenter रॉबर्ट मार्टिन के शब्दों में Use Case का प्रत्यक्ष पूर्वज है।
अक्सर पूछे जाने वाले प्रश्न
MVP में, Presenter ViewContract इंटरफ़ेस के माध्यम से View के साथ इंटरैक्ट करता है, सीधे नहीं। MVC में, Controller का IBOutlet/findViewById के माध्यम से View पर सीधा संदर्भ होता है। MVP Presenter को iOS Simulator या Android Emulator के बिना यूनिट टेस्ट के साथ परीक्षण करने की अनुमति देता है, क्योंकि Presenter UIKit या Android Framework पर निर्भर नहीं होता। MVC को कंट्रोलर के परीक्षण के लिए ऐप लॉन्च करने की आवश्यकता होती है।
MVP उन लीगेसी प्रोजेक्ट्स में उचित है जो पहले से इस पैटर्न पर बने हैं, और उन ऐप्स में जिनमें रिएक्टिव मैकेनिज़्म (LiveData, StateFlow, Combine) का समर्थन नहीं है। नए प्रोजेक्ट्स के लिए Google Jetpack के साथ MVVM (Android) और Apple SwiftUI के साथ MVVM (iOS) की सिफारिश करता है। MVP शुद्ध UIKit पर Combine के बिना उन प्रोजेक्ट्स के लिए सबसे अच्छा विकल्प बना हुआ है जहाँ व्यावसायिक लॉजिक के यूनिट टेस्ट की आवश्यकता होती है।
Android पर — retain फ़्रैगमेंट (setRetainInstance(true)) या Jetpack से ViewModel का उपयोग करें। Retain फ़्रैगमेंट घुमाने पर Presenter को संग्रहीत करता है और उसे नई Activity को भेजता है। Google का ViewModel एक आधुनिक विकल्प है जो retain फ़्रैगमेंट के बिना घुमाने पर स्वचालित रूप से स्थिति बनाए रखता है। iOS पर — Presenter प्रत्येक viewDidLoad पर फिर से बनाया जाता है लेकिन एक अलग कोऑर्डिनेटर सेवा में कैश किया जाता है।
कम से कम 4: ViewContract इंटरफ़ेस, ViewContract कार्यान्वयन (Activity/Fragment), Presenter, Model (Repository). यदि Dagger/Hilt का उपयोग किया जाता है, तो एक DI मॉड्यूल जोड़ा जाता है। 50 स्क्रीन के लिए यह 200+ क्लासेस है। MVVM प्रति स्क्रीन 1 फ़ाइल कम करता है (ViewContract की आवश्यकता नहीं), MVI State और Intent क्लासेस जोड़ता है। क्लासेस की संख्या बड़े प्रोजेक्ट्स में MVP के खिलाफ मुख्य तर्क है।
Passive View — View में कोई लॉजिक नहीं है, Presenter पूरी तरह से स्थिति और डेटा का प्रबंधन करता है। Supervising Controller — View स्वयं सरल बाइंडिंग (data binding) करता है, Presenter जटिल परिदृश्यों में हस्तक्षेप करता है। मोबाइल डेवलपमेंट में Passive View का प्रभुत्व है — यह अधिकतम परीक्षण क्षमता और पूर्वानुमेयता प्रदान करता है। Supervising Controller का उपयोग वेब फ्रेमवर्क (ASP.NET Web Forms, GWT) में किया जाता है।
निष्कर्ष
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें