MVP (Model-View-Presenter) — একটি আর্কিটেকচারাল প্যাটার্ন যেখানে Presenter, ViewContract ইন্টারফেসের মাধ্যমে Model এবং View-এর মধ্যে মধ্যস্থতাকারী হিসেবে কাজ করে। MVC-এর বিপরীতে, যেখানে Controller সরাসরি UIKit-এর মাধ্যমে View নিয়ন্ত্রণ করে, Presenter ফ্রেমওয়ার্কের ওপর নির্ভরশীল নয় — এটি অ্যাবস্ট্রাকশনের মাধ্যমে কাজ করে, যা এটিকে Android SDK বা UIKit ছাড়া পরীক্ষাযোগ্য করে তোলে। Jetpack আসার আগে Android ডেভেলপমেন্টে MVP ব্যাপকভাবে ব্যবহৃত হয় এবং লিগ্যাসি প্রকল্পগুলির জন্য এটি প্রাসঙ্গিক রয়ে গেছে। আরও জানতে — মার্টিন ফাওলারের নিবন্ধ দেখুন।
মূল বিষয়
MVP (Model-View-Presenter) — একটি আর্কিটেকচারাল প্যাটার্ন যা মার্টিন ফাওলার ২০০০-এর দশকের শুরুতে ইউজার ইন্টারফেসের পরীক্ষাযোগ্যতা উন্নত করার জন্য 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-এ পুনরায় তৈরি হয় কিন্তু একটি পৃথক কোঅর্ডিনেটর সার্ভিসে ক্যাশ করা হয়।
সর্বনিম্ন ৪টি: 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 অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন