পাঠ ৩০ · ৫৭-এর মধ্যে · মডিউল ৭
Home / Courses / Mobile App Development / স্টেট ম্যানেজমেন্ট

রিঅ্যাক্টিভ স্টেট প্যাটার্ন — স্ট্রিম ও অবজার্ভেবল

Reactive state patterns — streams & observables
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • স্ট্রিম/অবজার্ভেবল প্যাটার্ন কী এবং এটি L28-এর Store থেকে কীভাবে আলাদা
  • একটি সত্যিকারের Stream ক্লাস — subscribe() ও emit()
  • .map(fn) অপারেটর — কীভাবে এটি একটি নতুন, স্বতন্ত্র ট্রান্সফর্মড স্ট্রিম তৈরি করে
  • একাধিক .map() চেইন করে র সেন্সর-ডেটা থেকে ধাপে ধাপে অর্থপূর্ণ তথ্যে রূপান্তর

১ · একটি সত্যিকারের Stream ক্লাস

স্ট্রিম / অবজার্ভেবলStream / Observableসময়ের সাথে সাথে একাধিকবার মান "এমিট" করতে পারা একটি ডেটা-উৎস — যেকোনো সংখ্যক সাবস্ক্রাইবার প্রতিটি এমিশনের সময় নোটিফাই হয়। L28-এর Store-এ আমরা একটি "বর্তমান state" রাখতাম; একটি Stream-এ কোনো স্থায়ী state নেই — এটি শুধু একটি নালী (pipe) যার মধ্য দিয়ে একের পর এক মান বয়ে যায়, আর যেকোনো সাবস্ক্রাইবার সেই মুহূর্তে যা এমিট হয় তা পায়।

Python
# একটি সত্যিকারের Stream/Observable -- subscribe() ও emit()

class Stream:
    def __init__(self):
        self._subscribers = []

    def subscribe(self, callback):
        self._subscribers.append(callback)
        return callback

    def emit(self, value):
        for callback in self._subscribers:
            callback(value)


# উদাহরণ: একটি এক্সিলারোমিটার সেন্সরের র (raw) রিডিং-এর ধারা
raw_shake_events = Stream()

received = []
raw_shake_events.subscribe(lambda v: received.append(v))

raw_shake_events.emit(0.2)
raw_shake_events.emit(0.5)
raw_shake_events.emit(9.8)

print(f"সাবস্ক্রাইবার যা যা পেয়েছে: {received}")

    
লক্ষ্য করুন Stream-এর নিজের কোনো "বর্তমান মান" সংরক্ষিত থাকে না — শুধু emit() কল হওয়া মাত্র সেই মুহূর্তের সাবস্ক্রাইবাররা তা পায়। নতুন কেউ পরে সাবস্ক্রাইব করলে আগের এমিশনগুলো সে মিস করবে — এটিই একটি সাধারণ স্ট্রিমের স্বভাব (L44-এ একই প্যাটার্ন সেন্সর ইভেন্টের জন্য পুনরায় ব্যবহৃত হবে)।

২ · .map() অপারেটর: একটি নতুন, ট্রান্সফর্মড স্ট্রিম

র সেন্সর/নেটওয়ার্ক ডেটা প্রায়ই সরাসরি UI-তে দেখানোর উপযোগী নয় — এটিকে অর্থপূর্ণ এককে রূপান্তর করতে হয়। .map(fn) মূল স্ট্রিমে সাবস্ক্রাইব করা একটি নতুন Stream ইনস্ট্যান্স ফেরত দেয়, যা প্রতিটি মূল এমিশনকে fn দিয়ে রূপান্তর করে নিজের সাবস্ক্রাইবারদের কাছে পাঠায়।

Python
class Stream:
    def __init__(self):
        self._subscribers = []

    def subscribe(self, callback):
        self._subscribers.append(callback)
        return callback

    def emit(self, value):
        for callback in self._subscribers:
            callback(value)

    def map(self, fn):
        mapped_stream = Stream()

        def relay(raw_value):
            mapped_stream.emit(fn(raw_value))

        self.subscribe(relay)
        return mapped_stream


def fahrenheit_to_celsius(f):
    return round((f - 32) * 5 / 9, 2)


# র তাপমাত্রা-সেন্সর রিডিং (ফারেনহাইট) -- ডিভাইসের একটি বহিরাগত তাপ-সেন্সর থেকে
raw_temp_stream = Stream()

# ধাপ ১: সেলসিয়াসে রূপান্তরিত একটি নতুন, স্বতন্ত্র স্ট্রিম
celsius_stream = raw_temp_stream.map(fahrenheit_to_celsius)

# ধাপ ২: সেলসিয়াস স্ট্রিমকে আবার ম্যাপ করে জ্বর-সতর্কতা (বুলিয়ান) স্ট্রিম -- .map() চেইন করা
fever_alert_stream = celsius_stream.map(lambda c: c > 37.0)

received_raw = []
received_celsius = []
received_fever_alert = []

raw_temp_stream.subscribe(lambda v: received_raw.append(v))
celsius_stream.subscribe(lambda v: received_celsius.append(v))
fever_alert_stream.subscribe(lambda v: received_fever_alert.append(v))

for reading_f in [98.6, 100.4, 68.0]:
    print(f"র সেন্সর রিডিং এমিট হলো: {reading_f}°F")
    raw_temp_stream.emit(reading_f)

print(f"\nraw_temp_stream-এর সাবস্ক্রাইবার যা পেয়েছে (অপরিবর্তিত র মান): {received_raw}")
print(f"celsius_stream-এর সাবস্ক্রাইবার যা পেয়েছে (ট্রান্সফর্মড, °C): {received_celsius}")
print(f"fever_alert_stream-এর সাবস্ক্রাইবার যা পেয়েছে (দ্বিগুণ-ট্রান্সফর্মড, বুলিয়ান): {received_fever_alert}")

proof = received_celsius == [37.0, 38.0, 20.0] and received_fever_alert == [False, True, False]
print(f"\nপ্রমাণ -- মূল স্ট্রিমে দেওয়া র °F মান নয়, বরং প্রতিটি ধাপে ট্রান্সফর্মড মানই সাবস্ক্রাইবার পেয়েছে: {proof}")
assert proof

    
celsius_stream-এর সাবস্ক্রাইবার কখনো 98.6-এর মতো একটি র ফারেনহাইট মান দেখে না — সে শুধু fahrenheit_to_celsius() দিয়ে রূপান্তরিত 37.0 মানই পায়। একইভাবে fever_alert_stream-এর সাবস্ক্রাইবার সরাসরি কোনো তাপমাত্রাই দেখে না — শুধু চূড়ান্ত বুলিয়ান সিদ্ধান্ত। প্রতিটি .map() ধাপ পরের ধাপ থেকে সম্পূর্ণ স্বাধীন ও স্বতন্ত্রভাবে টেস্টযোগ্য।
মূল কথা · Key takeaway

একটি Stream সময়ের সাথে আসা ঘটনার ধারা প্রতিনিধিত্ব করে, আর .map() সেই ধারার উপর ভিত্তি করে নতুন, রূপান্তরিত ধারা তৈরি করার একটি নিরাপদ, চেইনেবল উপায় — প্রতিটি ধাপে মূল স্ট্রিমকে স্পর্শ না করেই। মোবাইল অ্যাপে এই প্যাটার্ন সেন্সর ডেটা (L44), রিয়েল-টাইম আপডেট (L40), এবং ইনপুট ইভেন্টের (L05) মতো "চলমান" ডেটার জন্য L28-এর "বর্তমান স্ন্যাপশট"-ভিত্তিক Store-এর চেয়ে বেশি স্বাভাবিক মডেল।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ যদি fever_alert_stream-এর একটি সাবস্ক্রাইবার থাকে কিন্তু celsius_stream-এর কোনো সাবস্ক্রাইবার না থাকে, তাহলে কি fever_alert_stream এখনো কাজ করবে?

হ্যাঁ — celsius_stream.map(...) কল করার সময় ইতিমধ্যে celsius_stream.subscribe(relay) কল হয়ে গেছে (এটিই fever_alert_stream তৈরির সময় ঘটে), তাই celsius_stream-এর নিজস্ব _subscribers লিস্টে সেই relay ফাংশনটি থাকবেই — এটি একটি "ইউজার-ফেসিং" সাবস্ক্রাইবার না হলেও, চেইনটি সচল রাখার জন্য একটি অভ্যন্তরীণ সাবস্ক্রাইবার হিসেবে গণনা হয়।

প্র ০২ একটি সাধারণ Store (L28) আর একটি Stream-এর মধ্যে সবচেয়ে বড় পার্থক্য কী?

Store-এর সবসময় একটি "বর্তমান state" থাকে (store.state) — নতুন সাবস্ক্রাইবার চাইলে এখনই সেটি পড়তে পারে। Stream-এর কোনো সংরক্ষিত "বর্তমান মান" নেই — এটি শুধু একটি ধারাবাহিক ঘটনা-প্রবাহ; নতুন সাবস্ক্রাইবার শুধু ভবিষ্যতের এমিশনই পাবে, অতীতের কোনোটি নয় (যদি না আলাদাভাবে শেষ মান সংরক্ষণ করে রাখা হয়)।

প্র ০৩ উপরের কোড সেলে raw_temp_stream.emit(68.0)-এর ফলে fever_alert_stream-এ কী মান আসবে, এবং কেন?

False আসবে। কারণ fahrenheit_to_celsius(68.0) = 20.0°C, এবং 20.0 > 37.0 মিথ্যা — তাই fever_alert_stream-এর সাবস্ক্রাইবার False পাবে। প্রতিটি মান দুটি .map() ধাপ পার হয়ে আসে: প্রথমে ফারেনহাইট থেকে সেলসিয়াসে, তারপর সেলসিয়াস থেকে বুলিয়ানে।

অনুশীলন

  1. চিন্তা করুন: একটি GPS-ভিত্তিক ফিটনেস-ট্র্যাকিং অ্যাপে কোন কোন ডেটাকে স্ট্রিম হিসেবে মডেল করা স্বাভাবিক হবে (যেমন প্রতি সেকেন্ডের লোকেশন আপডেট), এবং কোনটিকে বরং L28-এর Store-এর মতো "একবারের স্ন্যাপশট" স্টেট হিসেবে রাখা ভালো (যেমন "রান শুরু হয়েছে কি না")?

    ধারাবাহিকভাবে আসা লোকেশন আপডেট, হার্ট-রেট রিডিং, বা এক্সিলারোমিটার ডেটা স্ট্রিম হিসেবে স্বাভাবিক — এগুলো সময়ের সাথে বারবার আসতে থাকে এবং প্রতিটি নতুন মান নিজে থেকেই গুরুত্বপূর্ণ। "রান শুরু হয়েছে কি না"-এর মতো একটি বুলিয়ান ফ্ল্যাগ, বা "মোট দূরত্ব"-এর মতো একটি ক্রমবর্ধমান সংখ্যা, Store-এর "বর্তমান state" হিসেবে রাখা ভালো — এটি একবারে একটিই সত্য মান, যা যেকোনো সময় পড়া দরকার।

  2. পরীক্ষা করুন: উপরের দ্বিতীয় কোড সেলে fever_alert_stream-এর নিচে আরেকটি চেইনড স্ট্রিম যোগ করুন — alert_label_stream = fever_alert_stream.map(lambda is_fever: "⚠️ জ্বর সতর্কতা" if is_fever else "স্বাভাবিক") — এটিতে সাবস্ক্রাইব করে দেখুন তিনটি রিডিং-এর জন্য কী কী লেবেল পাওয়া যায়।

    তিনটি রিডিং (98.6, 100.4, 68.0) থেকে fever_alert_stream-এ যথাক্রমে False, True, False আসে, তাই alert_label_stream-এর সাবস্ক্রাইবার পাবে ["স্বাভাবিক", "⚠️ জ্বর সতর্কতা", "স্বাভাবিক"] — তিনটি .map() একসাথে চেইন হয়ে একটি একক র সংখ্যাসূচক মানকে সরাসরি ব্যবহারযোগ্য একটি UI টেক্সটে রূপান্তর করেছে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ অ্যাপ লাইফসাইকেল, মোবাইল UI/UX, MVVM/MVI আর্কিটেকচার, নেভিগেশন, স্টেট ম্যানেজমেন্ট, লোকাল স্টোরেজ, ডিভাইস ফিচার ও ডিপ্লয়মেন্ট — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
  • Full-Stack Web Frameworks কোর্স সহোদর কোর্স ইউনিডাইরেকশনাল স্টেট ফ্লো ও অবজার্ভার প্যাটার্নের ওয়েব-প্রেক্ষাপট সেই কোর্সেই কভার হয়েছে — এই পাঠ মোবাইলের ক্রমাগত-ডেটা (সেন্সর, নেটওয়ার্ক) প্রেক্ষাপটে একই প্যাটার্নের সম্প্রসারণ।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics, Full-Stack Web Frameworks ও Mobile App Development — সব এক জায়গায়।
আগের পাঠ
লাইফসাইকেল-অ্যাওয়্যার স্টেট ম্যানেজমেন্ট