Observer Pattern: Subscribe করো, Bell বাজাও
Observer pattern শেখো YouTube subscribe আর bell icon-এর গল্প দিয়ে। TypeScript আর C#-এ event code, real software-এ ব্যবহার, diagram আর practice task সহ।
Subscribe করো, Bell বাজাও 🔔
ধরো সুমাইয়া class 10-এ পড়ে। সে Maths Made Easy নামে একটা ছোট YouTube channel চালায়। প্রতি রবিবার সন্ধ্যা ৬টায় সে একটা নতুন video upload করে। তার সেরা বন্ধু রুবেল channel-টা খুব ভালোবাসে। classmate নাসরিন শুধু exam-এর আগে দেখে। আর internet-এর কোনো এক কোণে একটা playlist robot চুপচাপ সব maths video collect করতে থাকে।
এখন ভাবো — রুবেল কীভাবে জানে নতুন video এসেছে?
Option এক: রুবেল প্রতি ঘণ্টায় YouTube খুলে সুমাইয়ার channel চেক করে। "নতুন video? না। নতুন video? না। এখনো না?" রবিবারে সে ৩০ বার চেক করে, আর ২৯ বারই কিছু নেই। সময় নষ্ট, mobile data নষ্ট, আর তারপরও news দেরিতে পায়। Programming-এ এই বাজে repeated checking-কে বলে polling — real life-এ যেটা বোকামি, code-এও ঠিক ততটাই বোকামি।
Option দুই — স্মার্ট পথ: রুবেল Subscribe চাপে আর bell icon-এ tap করে। ব্যস। এরপর সে নিশ্চিন্তে ভুলে যেতে পারে। সুমাইয়া "Trigonometry in 10 Minutes" upload করার সাথে সাথে YouTube তাকে notify করে। তার phone buzz করে। নাসরিনের email-ও আসে। playlist robot-ও জানে। সবাই একসাথে, automatically।
আর সুন্দর ব্যাপারটা হলো — সুমাইয়া কি রুবেলকে personally চেনে? না। সে কি subscriber-দের phone number ডায়েরিতে লিখে রাখে? না। সে শুধু upload করে। Channel একটা subscriber list রাখে, আর notification সবার কাছে পৌঁছে যায়। Subscriber যেকোনো সময় join করতে পারে, যেকোনো সময় চলে যেতে পারে — exam শেষে নাসরিন unsubscribe করলে তার জন্য buzzing বন্ধ। সুমাইয়ার upload-এর কাজ এতটুকুও বদলায় না।
এটাই হলো Observer pattern। Channel হলো Subject (Publisher-ও বলে)। রুবেল, নাসরিন আর robot হলো Observers (Subscriber-ও বলে)। Subject-এর একটা change — নতুন video — automatically সব observer-এর কাছে ছড়িয়ে পড়ে। কেউ poll করে না। কেউ কারো সাথে hard-wire হয়ে থাকে না।
আমরা এখন সুমাইয়ার channel code-এ বানাবো, নাসরিনকে gracefully চলে যেতে দেখবো, একটা বিখ্যাত memory-leak bug ধরবো, আর শিখবো কেন এটাকে অনেকে software-এর সবচেয়ে বেশি ব্যবহৃত design pattern বলে।
Observer Pattern আসলে কী?
সহজ definition:
Observer হলো একটা behavioral design pattern যেটা object-দের মধ্যে one-to-many relationship তৈরি করে। একটা object — Subject — dependent object-দের একটা list রাখে — Observers — আর তার state বদলালেই সবাইকে automatically notify করে।
তিনটা ছোট operation দিয়েই পুরো pattern চলে:
- subscribe(observer) — "আমাকে তোমার list-এ রাখো।" (Subscribe button চাপা।)
- unsubscribe(observer) — "আমাকে list থেকে বাদ দাও।" (Unsubscribe চাপা।)
- notify() — subject তার list ধরে হাঁটে আর প্রতিটা observer-এর
update()method call করে। (সবার phone-এ bell বাজে।)
এই pattern-এর মূল উদ্দেশ্য হলো decoupling। Subject তার observer-দের concrete class জানে না — সে শুধু জানে "আমার list-এ থাকা সবার একটা update() method আছে যেটা আমি call করতে পারি।" Observer-রাও জানে না subject ভেতরে কীভাবে কাজ করে — তারা শুধু জানে "কোনো news হলে আমাকে call করা হবে।" যেকোনো পক্ষ বদলাতে পারে, বাড়তে বা কমতে পারে — অন্যপক্ষ ভাঙবে না।
একই idea-র আরো কিছু নাম শুনবে: Event-Subscriber, Listener, বা loosely বলতে গেলে Publish-Subscribe। (পরে দেখবো strict pub-sub একটু আলাদা — পড়তে থাকো।)
মনে রাখার trick: Subject = YouTube channel, Observer = subscriber, notify = bell icon। Subscribe করলে পাবে, unsubscribe করলে পাবে না, আর channel কে তুমি সেটা জানার দরকার নেই। bell icon মনে থাকলে pattern মনে থাকবে।
সমস্যাটা কী — Hard-Wired Channel
আগে ব্যথাটা অনুভব করো। ধরো YouTube বাজেভাবে code করা, আর channel-কে প্রতিটা viewer type-কে personally, নাম ধরে নিজের code-এ জানাতে হচ্ছে:
// ❌ BAD: the channel is hard-wired to every concrete viewer
class Channel {
uploadVideo(title: string) {
console.log(`Uploaded: ${title}`);
// The channel must personally know and call EVERY interested party:
arjunsPhone.showNotification(title);
meerasEmail.sendMail(title);
schoolNoticeBoard.pinPoster(title);
statisticsPanel.increaseCount(title);
// Tomorrow a smartwatch app comes? EDIT THIS FILE AGAIN!
}
}এখানে কী সমস্যা?
- Channel প্রতিটা concrete receiver-এর উপর depend করে। Phone, email, notice board, statistics — channel-এর code সবাইকে import করে চেনে। নতুন receiver মানেই channel edit করতে হবে। এটা Open/Closed Principle ভাঙে (extension-এর জন্য open, modification-এর জন্য closed)।
- Runtime-এ control নেই। নাসরিন exam-এর পর unsubscribe করতে পারবে না। wiring code-এ fixed, data-এ না। তার notification বন্ধ করতে হলে কাউকে channel edit করে re-deploy করতে হবে!
- Channel দুটো কাজ করছে। তার real কাজ video manage করা। এখন সে receiver-দের registry-ও maintain করছে। একটা class-এ দুটো responsibility — এটা পরিচিত design smell।
- একমাত্র বিকল্প polling। Channel কাউকে notify না করলে প্রতিটা viewer বারবার জিজ্ঞেস করবে, "কিছু নতুন আছে? আছে?" — অপচয় আর দেরিতে news, রুবেলের ৩০ বার refresh-এর মতো।
দুটো structure-এর পার্থক্যটা দেখো:
Observer pattern-এ channel একটাই জিনিস জানে: "আমার কাছে একটা list আছে, আর list-এ থাকা সবাই update() বোঝে।" List-এ দুজন observer থাকুক বা দুই মিলিয়ন, phone হোক বা smartwatch বা এমন কিছু যা এখনো আবিষ্কার হয়নি — channel-এর code কখনো বদলায় না।
এটা ঠিক school notice board আর প্রতিটা student-কে personally জানানোর পার্থক্যের মতো। Principal একটা notice pin করে; যে "subscribe" করেছে (board-এর সামনে দিয়ে যায়) সে news পায়। Principal ৮০০ classroom-এ যায় না।
সুমাইয়া আজ একটা video upload করলে আসলে কতজন react করে? তোমার ভাবার চেয়ে অনেক বেশি:
প্রতিটা slice আলাদা observer family, আর সুমাইয়ার upload button তাদের কাউকেই নাম ধরে চেনে না। এটাই পুরো point।
কীভাবে কাজ করে, ধাপে ধাপে
Recipe টা হলো:
- Subject আর observer চিহ্নিত করো। Subject হলো যেটা বদলায় (channel, weather station, stock price)। Observer হলো যেগুলো react করে (display, logger, notification sender)।
- Observer interface declare করো। সাধারণত একটাই method:
update(...)। ঠিক করো কী data আসবে (push vs pull — নিচে দেখো)। - Subject-কে তিনটা operation দাও।
subscribe,unsubscribe, আরnotify, সাথে observer-দের internal list। - State বদলালেই
notify()call করো — আর state পুরোপুরি update হওয়ার পরেই, যাতে observer-রা পড়লে consistent value পায়। - Concrete observer implement করো। প্রতিটা একই news-এ নিজের মতো react করে।
- Wire করো আর clean up করো। Client code observer subscribe করে। আর — খুব গুরুত্বপূর্ণ — আর দরকার না হলে unsubscribe করে। কেন সেটা একটু পরে ভয়ঙ্করভাবে দেখবো।
সুমাইয়া upload করার মুহূর্তে message-by-message sequence:
আর সেই রবিবার মানুষের দৃষ্টিতে:
রুবেলের সারিতে সব 5 দেখো, polling জীবনের সাথে তুলনা করো। একবার subscribe, তারপর নিজের জীবন নিজে চালাও। Pattern observer-দের যে উপহার দেয় তা হলো চেক করার ঝামেলা থেকে মুক্তি।
Push নাকি Pull? 📨
একটা design choice এখানে গুরুত্বপূর্ণ: update()-এ কতটুকু information যাবে?
- Push model: subject নিজেই data পাঠায় —
update("Trigonometry in 10 Minutes")। দ্রুত আর সহজ, কিন্তু subject-কে অনুমান করতে হয় প্রতিটা observer-এর কী দরকার, আর সবাইকে একই packet পাঠায়। - Pull model: subject শুধু নিজেকে পাঠায় —
update(channel)— আর প্রতিটা observer channel-কে জিজ্ঞেস করে যা দরকার:channel.latestVideo(),channel.subscriberCount()। Flexible, কিন্তু observer-রা subject-এর API জানতে বাধ্য, যেটা একটু বেশি coupling। - Practical মাঝামাঝি পথ: একটা ছোট, immutable event object পাঠাও যেটায় দরকারি তথ্য আছে। C#-এর
EventArgsআর DOMEventobject ঠিক এভাবেই কাজ করে।
| প্রশ্ন | Push model | Pull model | Event object (মাঝামাঝি) |
|---|---|---|---|
| কে decide করে কী data যাবে? | Subject | প্রতিটা observer | Subject, কিন্তু structured packet-এ |
| Coupling | Observer-দের জন্য সবচেয়ে কম | Observer-কে subject API জানতে হবে | কম — শুধু event type shared |
| Risk | কিছু observer যা ignore করে তা পাঠায় | Observer mid-change state পড়তে পারে | একটু বেশি class লিখতে হয় |
| Real example | update(title) | update(this) তারপর channel.latestVideo() | DOM Event, C# EventArgs |
College corner: push বনাম pull আসলে একটা প্রশ্ন — generality-র cost কে বহন করবে। Pure push প্রতি observer-এর জন্য O(1) call কিন্তু subject-কে one-size-fits-all payload আগে থেকে compute করতে হয়; pure pull প্রতিটা observer নিজের মতো fetch করতে পারে কিন্তু একটা notification k-টা extra subject read-এ পরিণত হয়, আর async system-এ notification queue-এর পরে state বদলে গেলে consistency হারায়। GoF book বলে pull বেশি reusable আর push বেশি efficient। Production framework-রা প্রায় সবসময় immutable event-object compromise বেছে নেয়: একটাই allocation, notify করার সময় data frozen, subject-এ callback নেই।
Blueprint 📐
মুখস্থ রাখার মতো class structure — দুই পাশেই interface, concrete class-রা শুধু interface-এর মাধ্যমে মিলে:
একটা arrow দীর্ঘক্ষণ দেখো: Subject --> Observer interface-এর দিকে point করে। YouTubeChannel থেকে PhoneNotification-এর দিকে কোনো arrow নেই। Channel এমন observer type-কেও notify করতে পারবে যা এখনো তৈরিই হয়নি।
আর একটা observer তার জীবনে কিছু state-এ থাকে — আর একটা state হলো trap:
এই Zombie state মাথায় রাখো। একটু পরে এটার সাথে number নিয়ে দেখা হবে।
Real Code: TypeScript-এ সুমাইয়ার Channel
চলো Maths Made Easy বানাই — subscribe, notify আর unsubscribe — পুরো trio সহ।
// --- The Observer interface: anyone who wants the bell to ring ---
interface Subscriber {
readonly name: string;
update(channelName: string, videoTitle: string): void; // push model
}
// --- The Subject: Priya's channel ---
class YouTubeChannel {
private subscribers: Subscriber[] = [];
private videos: string[] = [];
constructor(public readonly name: string) {}
// 🔔 "Press subscribe"
subscribe(s: Subscriber): void {
if (!this.subscribers.includes(s)) {
this.subscribers.push(s);
console.log(`${s.name} subscribed to ${this.name}. 🔔`);
}
}
// 🔕 "Press unsubscribe"
unsubscribe(s: Subscriber): void {
this.subscribers = this.subscribers.filter((sub) => sub !== s);
console.log(`${s.name} unsubscribed from ${this.name}. 🔕`);
}
// Priya's real work — and the trigger for notifications
uploadVideo(title: string): void {
this.videos.push(title);
console.log(`\n${this.name} uploaded: "${title}"`);
this.notify(title);
}
// Walk the list and ring every bell.
// We loop over a COPY, so an observer may safely
// unsubscribe itself while being notified.
private notify(title: string): void {
for (const s of [...this.subscribers]) {
s.update(this.name, title);
}
}
}
// --- Concrete observers: each reacts in its own way ---
class PhoneNotification implements Subscriber {
constructor(public readonly name: string) {}
update(channel: string, title: string): void {
console.log(` 📱 ${this.name}'s phone buzzes: New on ${channel} — "${title}"`);
}
}
class EmailDigest implements Subscriber {
constructor(public readonly name: string) {}
update(channel: string, title: string): void {
console.log(` ✉️ Email queued for ${this.name}: ${channel} released "${title}"`);
}
}
class WatchLaterList implements Subscriber {
readonly name = "Watch-Later Bot";
private list: string[] = [];
update(_channel: string, title: string): void {
this.list.push(title);
console.log(` 🤖 Watch-Later Bot saved "${title}" (total: ${this.list.length})`);
}
}
// --- The story runs ---
const channel = new YouTubeChannel("Maths Made Easy");
const arjun = new PhoneNotification("Arjun");
const meera = new EmailDigest("Meera");
const bot = new WatchLaterList();
channel.subscribe(arjun); // Arjun presses the bell
channel.subscribe(meera); // Meera prefers email
channel.subscribe(bot); // a bot subscribes too — the channel doesn't care!
channel.uploadVideo("Trigonometry in 10 Minutes");
channel.unsubscribe(meera); // exams over, Meera unsubscribes
channel.uploadVideo("Algebra Tricks for Class 7");Output:
Arjun subscribed to Maths Made Easy. 🔔
Meera subscribed to Maths Made Easy. 🔔
Watch-Later Bot subscribed to Maths Made Easy. 🔔
Maths Made Easy uploaded: "Trigonometry in 10 Minutes"
📱 Arjun's phone buzzes: New on Maths Made Easy — "Trigonometry in 10 Minutes"
✉️ Email queued for Meera: Maths Made Easy released "Trigonometry in 10 Minutes"
🤖 Watch-Later Bot saved "Trigonometry in 10 Minutes" (total: 1)
Meera unsubscribed from Maths Made Easy. 🔕
Maths Made Easy uploaded: "Algebra Tricks for Class 7"
📱 Arjun's phone buzzes: New on Maths Made Easy — "Algebra Tricks for Class 7"
🤖 Watch-Later Bot saved "Algebra Tricks for Class 7" (total: 2)তিনটা জয় লক্ষ্য করো:
YouTubeChannelকোথাওPhoneNotification,EmailDigestবা কোনো concrete class-এর নাম বলে না। সে শুধুSubscriberinterface চেনে। একটা bot যোগ করলাম channel না ছুঁয়েই।- নাসরিন runtime-এ unsubscribe করলো — কোনো code edit নেই, re-deploy নেই। পরের notify তাকে simply skip করলো।
notify()list-এর একটা copy-এর উপর loop করে, তাই একটা observer notification-এর মাঝে নিজেই unsubscribe করলেও loop ভাঙে না।
একই Idea C#-এ — Language-এর ভেতরেই Event
C# এই pattern-কে এতটাই ভালোবাসে যে language-এ events হিসেবে built-in করে নিয়েছে। event keyword দিয়ে subscriber list, subscribe (+=), unsubscribe (-=), আর notify (event invoke করা) সব free-তে পাওয়া যায়:
using System;
// The event data — a small immutable packet (the practical push model)
public class VideoUploadedEventArgs : EventArgs
{
public string Title { get; }
public VideoUploadedEventArgs(string title) => Title = title;
}
// --- The Subject ---
public class YouTubeChannel
{
public string Name { get; }
public YouTubeChannel(string name) => Name = name;
// This ONE line replaces our whole subscribe/unsubscribe/notify machinery!
public event EventHandler<VideoUploadedEventArgs>? VideoUploaded;
public void UploadVideo(string title)
{
Console.WriteLine($"\n{Name} uploaded: \"{title}\"");
// notify(): invoke the event; C# calls every handler on the list
VideoUploaded?.Invoke(this, new VideoUploadedEventArgs(title));
}
}
// --- The story ---
public static class Program
{
public static void Main()
{
var channel = new YouTubeChannel("Maths Made Easy");
// subscribe with += (press the bell)
EventHandler<VideoUploadedEventArgs> arjunsPhone =
(sender, e) => Console.WriteLine($" 📱 Arjun's phone: \"{e.Title}\" is out!");
EventHandler<VideoUploadedEventArgs> meerasEmail =
(sender, e) => Console.WriteLine($" ✉️ Email for Meera: \"{e.Title}\"");
channel.VideoUploaded += arjunsPhone;
channel.VideoUploaded += meerasEmail;
channel.UploadVideo("Trigonometry in 10 Minutes"); // both react
channel.VideoUploaded -= meerasEmail; // unsubscribe with -=
channel.UploadVideo("Algebra Tricks for Class 7"); // only Arjun reacts
}
}তুমি এখন পর্যন্ত যত C# event ব্যবহার করেছো — প্রতিটা button.Click += ... — সেটাই Observer pattern language-এর পোশাক পরে আছে। Java-তে listener interface, JavaScript-এ addEventListener, Python-এ signal library — pattern এতটাই useful যে language-রাই সেটা adopt করে নিয়েছে।
আর Python-এ দশ লাইনে পুরো idea:
class Channel:
def __init__(self, name):
self.name = name
self.subscribers = [] # the list IS the pattern
def subscribe(self, fn):
self.subscribers.append(fn)
def unsubscribe(self, fn):
self.subscribers.remove(fn)
def upload(self, title):
print(f"{self.name} uploaded: {title}")
for fn in list(self.subscribers): # copy, same trick as TypeScript
fn(title)
channel = Channel("Maths Made Easy")
arjun = lambda t: print(f" Arjun's phone buzzes: {t}")
channel.subscribe(arjun)
channel.upload("Trigonometry in 10 Minutes")
channel.unsubscribe(arjun) # always pair subscribe with unsubscribe!
channel.upload("Algebra Tricks") # silence — Arjun has moved onZombie Listener Hunt 🧟
এখন প্রতিশ্রুতি দেওয়া horror story, number সহ। এটা real application-এ সবচেয়ে ব্যয়বহুল Observer bug, আর এর একটা official নামও আছে: lapsed listener problem।
ধরো একটা mobile app। একটা দীর্ঘস্থায়ী subject আছে — একটা global event bus যেটা app চলার পুরো সময় বেঁচে থাকে। আর আছে স্বল্পস্থায়ী observer — screen যেগুলো user খোলে আর বন্ধ করে। প্রতিটা screen খোলার সময় subscribe করে। আর programmer বন্ধ করার সময় unsubscribe করতে ভুলে যায়।
Subject-এর list এখন প্রতিটা মৃত screen-এর strong reference ধরে আছে। Garbage collector প্রতিটা দেখে বলে, "কেউ এখনো এটা point করছে — delete করতে পারবো না।" সেই screen ৩০০ বার খুলে বন্ধ করো, আর ৩০০টা zombie screen memory-তে বসে আছে, প্রতিটা এখনো প্রতিটা notification receive আর process করছে। App ধীরে ধীরে ভারী হয়ে crash করে।
দুই ভাগ্যের পার্থক্য দেখো:
উপরের line হলো ভুলে যাওয়া app — সরাসরি crash-এর দিকে। নিচের line হলো disciplined app: প্রতিটা subscribe-এর জোড়া unsubscribe আছে, তাই যেকোনো মুহূর্তে শুধু currently open screen-টাই শুনছে। একই feature, একই pattern, cleanup-এর একটাই missing line তাদের আলাদা করে।
College corner: leak হয় কারণ subscription expected ownership direction উল্টে দেয়। সাধারণত স্বল্পস্থায়ী object দীর্ঘস্থায়ী object-কে reference করে আর স্বাধীনভাবে মরতে পারে। Subscribe করলে দীর্ঘস্থায়ী subject স্বল্পস্থায়ী observer-এর strong reference রাখে — তাই observer-এর lifetime চুপচাপ subject-এর lifetime হয়ে যায়। তিনটা industrial cure: (1) deterministic cleanup hook — C#-এ IDisposable, React-এর useEffect থেকে return করা cleanup function, Angular-এর takeUntilDestroyed; (2) weak reference — Java-র WeakReference, C# weak event pattern — যেটা list এখনো point করলেও GC collect করতে দেয়, তবে delivery unpredictable হয়; (3) subscription object — RxJS একটা Subscription return করে যার unsubscribe() store করে রাখো আর call করো, cleanup-কে explicit, ownable value বানায়। Time cost-ও জানো: notify() subject-এর thread-এ O(n) synchronous call, তাই হাজারটা zombie শুধু memory নষ্ট করে না — প্রতিটা upload slow করে।
কোথায় ব্যবহার করবে?
দ্রুত scan করার table:
| Situation | Observer ব্যবহার করবে? | কেন |
|---|---|---|
| একটা change অনেক reactor-এ পৌঁছাতে হবে, সংখ্যা আগে থেকে জানা নেই | হ্যাঁ | Subscriber list যেকোনো সংখ্যা handle করে |
| Listener runtime-এ আসবে আর যাবে | হ্যাঁ | subscribe/unsubscribe সস্তা |
| Polling এড়াতে চাও ("কিছু নতুন আছে?") | হ্যাঁ | Subject তখনই push করে যখন সত্যিই news হয় |
| Subject concrete reactor class-এর উপর depend করবে না | হ্যাঁ | সে শুধু Observer interface দেখে |
| ঠিক একটা fixed receiver, চিরকাল | না | সরাসরি method call সহজ আর পরিষ্কার |
| Observer-রা কঠোর, guaranteed order-এ চলবে | সাবধান | Plain Observer weak ordering দেয় |
| সব observer একসাথে succeed বা fail করবে (transaction) | না | Partial failure Observer-এ normal; অন্য machinery দরকার |
Real Software-এ কোথায় দেখবে
Observer pattern হয়তো real code-এ সবচেয়ে বেশি ব্যবহৃত pattern:
- DOM event listener। Browser-এ প্রতিটা
button.addEventListener("click", handler)একটা observer-কে subject-এ subscribe করে;removeEventListenerunsubscribe করে। 2017 থেকে browser-রা constructableEventTarget-ও expose করে যাতে তোমার নিজের object subject হতে পারে। - React আর state management। State বদলালে সেটার উপর depend করা component-রা notify পায় আর re-render হয়। Redux-এর মতো library-তে component store-কে subscribe করে; একটা dispatch সব connected component-এ ছড়িয়ে পড়ে।
useEffect-এর cleanup function আছে ঠিক zombie listener মারার জন্যই। - RxJS আর Angular। RxJS পুরো একটা library observable-এর উপর দাঁড়িয়ে — Observer pattern-এ superpowers যেমন stream merge করা, search box debounce করা, failure-এ retry করা। Angular সাথে করেই ship করে।
- MQTT আর message broker। IoT device topic-এ publish করে; subscriber broker-এর মাধ্যমে selected message পায়। এটা Observer-এর broker-style pub-sub বড় ভাই, chat app, live cricket score আর GPS tracking-এ ব্যবহার হয়।
- Newsletter আর notification system। Email list, push-notification service আর সত্যিকারের YouTube bell — internet scale-এ observer list।
- Open-source example। java-design-patterns repository-তে runnable example আছে, আর Refactoring Guru দশটা language-এ দেখায়।
Students যেসব ভুল করে ⚠️
ভুল ১: Unsubscribe করতে ভুলে যাওয়া — real app-এ যে memory leak তোমাকে তাড়া করবে। Figure 7-এর zombie গল্প এক paragraph-এ। দীর্ঘস্থায়ী subject তার list-এর প্রতিটা observer-এর strong reference রাখে। স্বল্পস্থায়ী observer (বন্ধ screen, শেষ হওয়া task) কখনো unsubscribe না করলে garbage collector কখনো free করতে পারবে না। সে memory-তে থাকবে, প্রতিটা notification receive আর process করতে থাকবে, চিরকাল। ৫০০ বার screen খুললে ৫০০টা zombie শুনছে। সমাধান: প্রতিটা subscribe-এর সাথে unsubscribe pair করো (dispose-এ, useEffect cleanup-এ, IDisposable দিয়ে), বা language support থাকলে weak reference ব্যবহার করো। নিয়মটা জোরে বলো: subscribe ছাড়া unsubscribe-এর plan নেই।
ভুল ২: State ready হওয়ার আগেই notify করা। Subject-এর update অর্ধেক হতে হতে notify() call করলে, যে observer-রা data pull করে তারা half-updated, inconsistent state পড়বে — video title আছে কিন্তু video নেই। সবসময় আগে update শেষ করো; সবশেষে bell বাজাও।
আরো কিছু দেখো:
- Notification-এর মাঝে list modify করা snapshot copy ছাড়া — observer skip হতে পারে বা loop crash করতে পারে। আমাদের code-এর মতো
[...subscribers]-এর উপর notify করো। - একটা খারাপ observer বাকি সবাইকে মেরে ফেলা। Observer #2 exception throw করলে observer #3 থেকে #10 news নাও পেতে পারে। Observer untrusted হলে প্রতিটা call try/catch দিয়ে wrap করো।
- Update storm আর loop। Observer A-এর update একটা subject বদলায় যেটা B দেখছে, B-এর update একটা subject বদলায় যেটা A দেখছে... stack overflow না হওয়া পর্যন্ত infinite ping-pong। Update handler ছোট রাখো আর ভেতরে অন্য subject-এ write করা এড়াও।
Cousins-দের সাথে তুলনা
দুটো comparison প্রতিটা student-কে জানতে হবে: Observer বনাম Pub-Sub, আর Observer বনাম Mediator।
| প্রশ্ন | Observer (classic) | Publish-Subscribe (broker style) | Mediator |
|---|---|---|---|
| মাঝখানে কে? | কেউ না — subject নিজেই list ধরে | Broker / event bus / topic channel | Mediator যে coordination logic রাখে |
| দুই পক্ষ কি পরিচিত? | Interface-এর মাধ্যমে, হ্যাঁ (direct wiring) | একেবারেই না — শুধু topic name | Component শুধু mediator চেনে |
| Same process? | সাধারণত হ্যাঁ, same call stack | প্রায়ই আলাদা process বা machine | হ্যাঁ |
| Delivery | Synchronous, immediate | প্রায়ই asynchronous, queued | Mediator decide করে |
| Direction | One-way broadcast | Topic দিয়ে one-way | Two-way conversation |
| Real-life ছবি | Channel নিজের subscriber list call করে | Post office address দিয়ে চিঠি sort করে | Air traffic control tower |
এক বাক্যে nuance: প্রতিটা pub-sub observer-like, কিন্তু classic Observer broker-less — subject নিজেই list রাখে আর bell বাজায়। যেই মুহূর্তে topic-সহ একটা middleman আসে, আর publisher-subscriber একে অপরকে চেনে না, তখন তুমি true pub-sub-এ (MQTT, Kafka, event bus) ঢুকে গেছো। Design Gurus comparison এই line ভালো explain করে।
আর Mediator-এর সাথে: Observer বলে "অনেক listener-এ একটা change ছড়িয়ে দাও।" Mediator বলে "many-to-many conversation এক brain-এ centralize করো।" মজার ব্যাপার হলো, mediator প্রায়ই ভেতরে observer দিয়ে implement হয় — tower তার plane-দের event subscribe করে।
পুরো Pattern এক ছবিতে 🗺️
Quick Revision Box
+--------------------------------------------------------------+
| OBSERVER — QUICK REVISION |
+--------------------------------------------------------------+
| What : One Subject notifies MANY Observers |
| automatically when its state changes. |
| Picture : YouTube channel + Subscribe button + bell icon. |
| Trio : subscribe(o), unsubscribe(o), notify() |
| Models : Push = send the data; Pull = send yourself, |
| observer asks back; Middle = small event object. |
| Wins : No polling, loose coupling, listeners join and |
| leave at runtime, Open/Closed friendly. |
| Danger #1: FORGOTTEN UNSUBSCRIBE = memory leak |
| (the lapsed listener problem). Always clean up! |
| Dangers : notify before state ready, update storms, |
| one failing observer blocking the rest. |
| Cousins : Pub-Sub = adds a broker + topics (async, remote); |
| Mediator = two-way coordination brain. |
| Used in : addEventListener, C# events, React/Redux, RxJS, |
| MQTT, push notifications. |
+--------------------------------------------------------------+Practice করো ✏️
নিজে চেষ্টা করো। উপরের TypeScript channel code থেকে শুরু করো।
-
Cricket score update। একটা
CricketMatchsubject বানাও যেটা score রাখে। Observer:MobileApp("IND 245/3" দেখায়),StadiumScreen(বড় text দেখায়), আরCommentator(প্রতিটা boundary-তে excited sentence print করে)। কয়েকবারmatch.updateScore(runs, wickets)call করো। তারপর innings break-এCommentatorunsubscribe করো আর silence confirm করো। -
Leak hunt। Task 1-এর solution নাও। Loop-এ ১,০০০টা
MobileAppobserver create করে subscribe করো, কিন্তু unsubscribe "ভুলে যাও", আর শুধু শেষেরটা variable-এ রাখো। একটা counter যোগ করো যেটা প্রতিটা update-এ কতজন observer notify হলো print করে। দেখো সংখ্যাটা ১,০০০-এ থাকছে — এগুলোই Figure 7-এর zombie listener! এখন fix করো: পরের app বানানোর আগে আগেরটা unsubscribe করো, আর counter-এর behavior দেখো। এক বাক্যে লেখো কেন এটা long-running app-এ ছোট script-এর চেয়ে বেশি matter করে। -
Push থেকে pull।
YouTubeChannel-কে push থেকে pull-এ convert করো:update()channel নিজেই receive করবে, আর প্রতিটা observerchannel.latestVideo()call করবে। তারপর দুই বাক্যে দুই version-এর coupling তুলনা করো। কোন observer বেশি powerful হলো, আর কী নতুন risk নিলো? -
School notice board (challenge)। একটা
NoticeBoardsubject model করো topic সহ: "sports", "exams", "holidays"। Student শুধু যে topic-এ আগ্রহী সেটায় subscribe করে, যেমনboard.subscribe("exams", rahim)। Principal যখন কোনো topic-এ notice post করে, শুধু সেই topic-এর subscriber-রাই notify পায়। ভাবার প্রশ্ন: topic আর middleman board যোগ করে তুমি কোন দিকে যাচ্ছো — classic Observer, নাকি Publish-Subscribe? দুই বাক্যে explain করো। -
Weak bell (college)। তোমার language-এর weak reference tool research করো (JavaScript-এ
WeakRef, Java/C#-এWeakReference)। Subscriber list-কে weak reference ধরে রাখতে rewrite করো, যাতে unsubscribe না করলেও zombie listener garbage collect হতে পারে। তারপর তিন বাক্যে নতুন সমস্যাটা লেখো: ঠিক কখন একটা live observer চুপচাপ notification miss করতে পারে?
সচরাচর জিজ্ঞাসা
- Observer pattern কী — এক লাইনে বলো।
- এটা একটা behavioral design pattern যেখানে একটা object (subject) interested object-দের একটা list রাখে (observers) আর তার state বদলালেই সবাইকে automatically জানায় — ঠিক YouTube channel যেভাবে subscriber-দের notify করে।
- Observer আর Publish-Subscribe-এর মধ্যে পার্থক্য কী?
- Classic Observer-এ subject নিজেই observer-দের list ধরে রাখে আর সরাসরি call করে। True Pub-Sub-এ publisher আর subscriber-দের মাঝখানে একটা middleman (broker বা event bus) থাকে — দুই পক্ষ একে অপরকে চেনেই না, এমনকি আলাদা machine-এও থাকতে পারে।
- Observer-কে কেন unsubscribe করতে হয়?
- যদি একটা দীর্ঘস্থায়ী subject একটা আর-দরকার-নেই observer-কে strong reference হিসেবে ধরে রাখে, তাহলে garbage collector সেটা কখনো free করতে পারবে না। এই 'lapsed listener' সমস্যাটা memory leak-এর সবচেয়ে common কারণগুলোর একটা।
- Push model আর pull model কী?
- Push-এ subject বদলানো data argument হিসেবে update()-এ পাঠায়। Pull-এ subject শুধু notification পাঠায়, আর প্রতিটা observer নিজের দরকারমতো subject-কে জিজ্ঞেস করে data নেয়। বেশিরভাগ real system একটা ছোট event object পাঠিয়ে দুটোর মাঝামাঝি পথ নেয়।
- Observer pattern কোথায় কোথায় দেখা যায় everyday code-এ?
- Browser-এ DOM addEventListener, C# events, React-এ state change হলে re-rendering, RxJS observables, আর YouTube bell-এর মতো notification system — সব জায়গায় observer idea কাজ করছে।
আরো দেখো
সম্পর্কিত পাঠ
Mediator Pattern: Control Tower যেটা ক্যাওস থামায়
বিমানবন্দরের control tower-এর গল্প দিয়ে Mediator pattern শেখো — সহজ TypeScript আর C# কোড, MediatR উদাহরণ, diagram, table আর practice task সহ।
Command Pattern: প্রতিটি কাজকে একটি অর্ডার স্লিপে বদলে দাও
রেস্তোরাঁর অর্ডার স্লিপের গল্প দিয়ে Command pattern শেখো। TypeScript আর C#-এ undo ও redo সহ পুরো কোড, diagram, table, আর practice task।
Chain of Responsibility Pattern: যে যেটা পারে সে সামলাও, নইলে পাস করো
Chain of Responsibility pattern শেখো একটা school leave application-এর গল্পের মাধ্যমে — সহজ TypeScript আর C# code, diagram, table, আর practice task সহ।
Memento Pattern: বস ফাইটের আগে গেম সেভ করো
ভিডিও গেমের save point-এর গল্প দিয়ে Memento pattern বোঝো — TypeScript আর Python-এ undo-র উদাহরণ, diagram, table আর সহজ practice task সহ।