MVC ও MVVM প্যাটার্ন
এই পাঠে যা শিখবেন
- MVC-এর তিনটি অংশ — Model, View, Controller — প্রতিটির স্পষ্ট, আলাদা দায়িত্ব
- একটি ইউজার ইন্টারঅ্যাকশন MVC-র মধ্য দিয়ে ঠিক কোন ক্রমে প্রবাহিত হয়
- MVVM কীভাবে Controller-কে ViewModel দিয়ে প্রতিস্থাপন করে, এবং মূল পার্থক্যটি কী
- একটি বাস্তব Python MVC Todo অ্যাপ বানিয়ে সম্পূর্ণ ডেটা ফ্লো হাতে-কলমে দেখা
১ · MVC — তিনটি স্পষ্ট দায়িত্ব
MVCModel-View-Controllerএকটি আর্কিটেকচারাল প্যাটার্ন যা ইন্টারঅ্যাক্টিভ অ্যাপ্লিকেশনকে তিনটি ভাগে ভাগ করে — Model (ডেটা), View (প্রদর্শন), Controller (ইনপুট হ্যান্ডলিং)। একটি genuinely ব্যাপকভাবে ব্যবহৃত, বাস্তব আর্কিটেকচারাল প্যাটার্ন — ইন্টারঅ্যাক্টিভ অ্যাপ্লিকেশনগুলোকে তিনটি স্পষ্ট অংশে ভাগ করে (L14-এর separation-of-concerns থিমের সরাসরি প্রয়োগ):
অ্যাপ্লিকেশনের ডেটা ও বিজনেস লজিক ধরে রাখে — কোনো UI-সংক্রান্ত ধারণা ছাড়াই, সম্পূর্ণ স্বাধীন।
Model-এর ডেটা ইউজারকে দেখায়/রেন্ডার করে — যতটা সম্ভব "dumb"/প্যাসিভ রাখা হয়, নিজে কোনো সিদ্ধান্ত নেয় না।
ইউজার ইনপুট হ্যান্ডল করে, সেই অনুযায়ী Model আপডেট করে, এবং View-কে নির্বাচন/আপডেট করে — অন্য দুইয়ের মধ্যে coordinator।
স্বাভাবিক ডেটা ফ্লো এভাবে: ইউজার View-এর সাথে ইন্টারঅ্যাক্ট করে → View সেই ইনপুট Controller-এ পাঠায় → Controller Model আপডেট করে → Model-এর পরিবর্তন View-কে আপডেট হতে ট্রিগার করে। শেষ ধাপটি লক্ষ্য করার মতো — অনেক বাস্তব MVC বাস্তবায়নে View সরাসরি Model-কে observe করে (M5/L23-এর Observer প্যাটার্নের সরাসরি পুনর্ব্যবহার), Controller নিজে সরাসরি View-তে push করে না।
এই তিনটি দায়িত্ব আলাদা করার ফল — একই Model একাধিক ভিন্ন View-তে দেখানো যায় (যেমন একটি ওয়েব View আর একটি মোবাইল View), বিজনেস লজিক কোথাও ডুপ্লিকেট না করেই — এটি L15-এর DRY নীতির সরাসরি প্রয়োগ, কিন্তু এখন আর্কিটেকচার লেভেলে।
২ · MVVM — Controller-এর বদলে ViewModel
MVVMModel-View-ViewModelMVC-এর একটি রূপভেদ, যেখানে Controller-এর বদলে একটি ViewModel থাকে যা Model-এর ডেটা View-ফ্রেন্ডলি ফরম্যাটে এক্সপোজ করে, আর View সরাসরি তার সাথে বাইন্ড করে। MVC-এর একটি real, বহুল-ব্যবহৃত রূপভেদ — বিশেষত আধুনিক UI ফ্রেমওয়ার্কে। কাঠামো প্রায় একই থাকে, কিন্তু Controller-এর জায়গায় একটি ViewModel বসে — এটি Model-এর ডেটাকে View-ফ্রেন্ডলি, প্রি-ফরম্যাটেড রূপে এক্সপোজ করে এবং View-নির্দিষ্ট প্রেজেন্টেশন লজিক সামলায়, আর View সরাসরি ViewModel-এর এক্সপোজ করা প্রপার্টিগুলোর সাথে বাইন্ড করে (একটি ডেটা-বাইন্ডিং মেকানিজম, শুধু নামমাত্র উল্লেখ) — Controller ম্যানুয়ালি প্রতিটি আপডেট push করার বদলে।
মূল পার্থক্যটি নির্দিষ্ট করে বলা যাক: MVVM-এর View সাধারণত তার ViewModel-এর সাথে আরও ডিক্লারেটিভভাবে সংযুক্ত থাকে (স্বয়ংক্রিয় বাইন্ডিং), MVC-র তুলনায় — MVC-তে Controller বেশিরভাগ ক্ষেত্রে ম্যানুয়ালি View আপডেট মধ্যস্থতা করে।
৩ · একটি বাস্তব MVC — Todo অ্যাপ্লিকেশন
নিচের কোড সেলে একটি সম্পূর্ণ, ordinary Python OOP MVC ট্রায়ো বানানো হয়েছে — এটি কোনো সিমুলেশন নয়, বাস্তব,
সম্পূর্ণ কার্যকরী কোড: একটি TodoModel (ডেটা + Observer সাপোর্ট), একটি TodoView
(print-ভিত্তিক রেন্ডারার, Model-এ সাবস্ক্রাইবড), আর একটি TodoController (Model আপডেট করার
মেথড এক্সপোজ করে)।
class TodoModel:
def __init__(self):
self.items = [] # প্রতিটি item: {"text": ..., "done": False}
self.observers = [] # M5/L23-এর Observer প্যাটার্নের সরাসরি পুনর্ব্যবহার
def subscribe(self, observer):
self.observers.append(observer)
def _notify(self):
for obs in self.observers:
obs.render(self.items)
def add_item(self, text):
self.items.append({"text": text, "done": False})
self._notify() # Model বদলাতেই স্বয়ংক্রিয়ভাবে View-কে জানানো হচ্ছে
def complete_item(self, index):
self.items[index]["done"] = True
self._notify()
class TodoView:
def render(self, items):
print("---- Todo List (View) ----")
for i, item in enumerate(items):
mark = "x" if item["done"] else " "
print(f"[{mark}] {i}: {item['text']}")
print()
class TodoController:
def __init__(self, model):
self.model = model
def add_todo(self, text):
self.model.add_item(text)
def complete_todo(self, index):
self.model.complete_item(index)
model = TodoModel()
view = TodoView()
model.subscribe(view) # View, Model-কে observe করছে
controller = TodoController(model)
controller.add_todo("Git শেখা")
controller.add_todo("MVC পাঠ শেষ করা")
controller.complete_todo(0)
TodoController-এর কোনো মেথডেই print() কল নেই — Controller শুধু
Model আপডেট করে, বাকিটা _notify()-এর মাধ্যমে স্বয়ংক্রিয়ভাবে ঘটে। প্রতিটি
add_todo/complete_todo কলের পর View স্বয়ংক্রিয়ভাবে পুরো লিস্টের সর্বশেষ অবস্থা
প্রিন্ট করে — এটিই MVC-র "Model বদলালে View নিজে থেকে আপডেট হয়" নীতির concrete প্রমাণ।
MVC তিনটি স্পষ্ট দায়িত্বে ভাগ করে — Model (ডেটা), View (প্রদর্শন), Controller (ইনপুট) — আর View-কে Model-এর Observer বানিয়ে দেয়, যাতে ম্যানুয়াল push ছাড়াই আপডেট প্রচারিত হয়। MVVM একই কাঠামো, কিন্তু Controller-এর জায়গায় একটি ViewModel বসিয়ে View-ViewModel সংযোগকে আরও ডিক্লারেটিভ বানায়। দুটোই একই মূল লক্ষ্য অর্জন করে — বিজনেস লজিক থেকে প্রদর্শন-লজিক আলাদা রাখা, যাতে একই Model একাধিক View-তে পুনর্ব্যবহারযোগ্য থাকে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
উপরের কোড সেলে TodoView.render() মেথডটি সরাসরি কোথাও কল করা হয়নি, তবুও এটি তিনবার চলে। কেন?
কারণ view অবজেক্টটি model.subscribe(view)-এর মাধ্যমে Model-এর observer
লিস্টে যোগ হয়েছে — প্রতিবার add_item বা complete_item কল হলে Model নিজে
self._notify() কল করে, যা প্রতিটি সাবস্ক্রাইবড observer-এর (এখানে শুধু view)
render() মেথড কল করে। Controller বা কোনো ব্যবহারকারী কোড কখনো সরাসরি
view.render() কল করেনি — এটিই Observer-চালিত স্বয়ংক্রিয় আপডেট।
প্র ০২ MVVM-এ View আর ViewModel-এর মধ্যে সংযোগকে "বেশি ডিক্লারেটিভ" বলা হয় — এর মানে ঠিক কী, MVC-র Controller-ভিত্তিক সংযোগের তুলনায়?
MVC-তে Controller প্রতিটি ক্ষেত্রে ম্যানুয়ালি সিদ্ধান্ত নেয় ঠিক কখন ও কীভাবে View আপডেট হবে (উপরের কোড সেলে এটি Observer-এর মাধ্যমে হচ্ছে, কিন্তু অনেক সরল MVC বাস্তবায়নে Controller নিজেই সরাসরি View-তে push করে)। MVVM-এ View শুধু ঘোষণা করে "আমার এই অংশটি ViewModel-এর এই প্রপার্টির সাথে বাইন্ডেড" — ফ্রেমওয়ার্ক নিজে থেকেই সেই প্রপার্টি বদলালে View আপডেট করে, কোনো ম্যানুয়াল push কোড লেখা ছাড়াই।
প্র ০৩
যদি একই TodoModel-এ একটি দ্বিতীয় View (ধরুন একটি JSON-এক্সপোর্ট করা View) সাবস্ক্রাইব করানো হয়, তাহলে TodoController-এর কোডে কী পরিবর্তন লাগবে?
কিছুই না। TodoController শুধু self.model.add_item(...)/
self.model.complete_item(...) কল করে — Model-এ কতগুলো বা কী ধরনের View সাবস্ক্রাইবড আছে,
সে বিষয়ে Controller-এর কোনো ধারণাই নেই। এটিই এই পাঠের মূল সুবিধার সরাসরি প্রমাণ — একাধিক View একই
Model শেয়ার করতে পারে, Controller বা Model-এর কোডে কোনো পরিবর্তন ছাড়াই।
অনুশীলন
-
চিন্তা করুন: একটি ব্লগ অ্যাপ্লিকেশনে MVC প্রয়োগ করলে Model, View, Controller-এ ঠিক কী কী থাকবে তার একটি সংক্ষিপ্ত তালিকা লিখুন (উদাহরণ: পোস্ট ডেটা, HTML রেন্ডারিং, "নতুন কমেন্ট" ফর্ম হ্যান্ডলিং)।
সম্ভাব্য বিভাজন: Model — পোস্টের টেক্সট, লেখক, কমেন্ট লিস্ট, এবং পোস্ট সেভ/লোড/ভ্যালিডেট করার বিজনেস লজিক। View — পোস্টের HTML রেন্ডারিং, কমেন্ট ফর্মের প্রদর্শন। Controller — "নতুন কমেন্ট জমা দিন" ফর্ম সাবমিশন হ্যান্ডল করা, ইনপুট পড়ে Model-এর
add_comment(...)-জাতীয় মেথড কল করা, এবং কোন View রেন্ডার হবে তা ঠিক করা। -
পরীক্ষা করুন: উপরের কোড সেলে
TodoModel-এ একটিremove_item(index)মেথড যোগ করুন (যা_notify()-ও কল করে), এবংTodoController-এ একটি সংশ্লিষ্টremove_todo(index)মেথড যোগ করে একটি আইটেম মুছে দেখুন — View স্বয়ংক্রিয়ভাবে আপডেটেড লিস্ট দেখাচ্ছে কিনা যাচাই করুন।remove_itemমেথডটিself.items.pop(index)(বা অনুরূপ) করে তারপরself._notify()কল করলেই যথেষ্ট — যেহেতুviewইতিমধ্যে সাবস্ক্রাইবড, তাই এই নতুন মেথডটিও স্বয়ংক্রিয়ভাবে View আপডেট ট্রিগার করবে, বাড়তি কোনো কোড ছাড়াই — Observer-চালিত ডিজাইনের একটি সরাসরি সম্প্রসারণ, নতুন কোনো wiring ছাড়াই।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ · ভার্সন কন্ট্রোল বেসিকস L29 M6 শেষ — এবার M7 শুরু, সেন্ট্রালাইজড বনাম ডিস্ট্রিবিউটেড ভার্সন কন্ট্রোল দিয়ে Git ফাউন্ডেশনের যাত্রা।
- বিহেভিয়ারাল প্যাটার্ন — Observer ও Strategy L23 এই পাঠে ব্যবহৃত Observer প্যাটার্নের মূল, ক্লাস-লেভেল সংজ্ঞাটি আবার দেখে নিন।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ M7-M9-এ পুরো Git — ফাউন্ডেশন, ব্রাঞ্চিং/মার্জিং ও কোলাবোরেশন ওয়ার্কফ্লো।