নেভিগেশন স্টেট ও ব্যাক-স্ট্যাক ম্যানেজমেন্ট
এই পাঠে যা শিখবেন
- কেন একটি ইন-মেমোরি নেভিগেশন স্ট্যাক M1-এর
killedট্রানজিশনে স্বয়ংক্রিয়ভাবে বেঁচে থাকে না serialize_stack()— একটি লাইভNavigationStack-কে একটি স্বাধীন, সরল তালিকায় রূপান্তরrestore_stack()— সেই সংরক্ষিত তালিকা থেকে একটি সম্পূর্ণ নতুন, কার্যকরNavigationStackপুনর্গঠন- equality check ও আচরণগত (behavioral) পরীক্ষা দিয়ে নিশ্চিত করা রিস্টোর করা স্ট্যাক আসল স্ট্যাকের মতোই নির্ভরযোগ্য
১ · সমস্যা: নেভিগেশন স্ট্যাক মেমোরিতেই থাকে, কিন্তু killed মেমোরি মুছে দেয়
L24-এর NavigationStack ক্লাসটি ভেতরে একটি সাধারণ Python list — এবং যেকোনো
ইন-মেমোরি ডেটার মতো, অ্যাপ প্রসেস killed স্টেটে গেলে (M1-এ শেখা লাইফসাইকেল ট্রানজিশন) এই
list-ও পুরোপুরি মুছে যায়। ব্যবহারকারী যখন অ্যাপটি আবার খোলেন, OS একটি সম্পূর্ণ নতুন প্রসেস চালু
করে — কোনো পুরনো নেভিগেশন হিস্ট্রি স্বয়ংক্রিয়ভাবে ফিরে আসে না, যদি না অ্যাপ নিজে থেকে সেটি
killed-এ যাওয়ার আগে কোথাও (ডিস্কে) সংরক্ষণ করে রাখে এবং রিলঞ্চের সময় সেখান থেকে পড়ে নেয়। এই
পাঠে সেই সংরক্ষণ ও পুনরুদ্ধার প্রক্রিয়াটি সিমুলেট করা হবে — ডিস্কে প্রকৃত লেখা এই স্যান্ডবক্সে সম্ভব নয়
(M8-এ (L32) বাস্তব key-value স্টোরেজ এপিআই কভার হবে), তাই এখানে একটি সাধারণ Python ভ্যারিয়েবল দিয়ে
"সংরক্ষিত ডেটা"-কে প্রতিনিধিত্ব করা হয়েছে।
killed ট্রানজিশনের পরও বেঁচে থাকে — restore_stack() এটি দিয়ে একটি নতুন, সমানভাবে কার্যকর স্ট্যাক পুনর্গঠন করে।২ · serialize_stack() — লাইভ স্ট্যাক থেকে একটি স্বাধীন স্ন্যাপশট
সিরিয়ালাইজেশন এখানে খুব সরল — শুধু স্ক্রিন-নামের একটি নতুন list তৈরি করা, যা মূল
nav.stack থেকে স্বাধীন (একই অবজেক্টের রেফারেন্স নয়)। নিচের সেলে একটি নেভিগেশন
সেশন তৈরি করে সিরিয়ালাইজ করা হয়েছে।
# L24-এর NavigationStack, এই পাতায় নতুন করে সংজ্ঞায়িত
class NavigationStack:
def __init__(self, root_screen):
self.stack = [root_screen]
def push(self, screen):
self.stack.append(screen)
def pop(self):
if len(self.stack) <= 1:
return None
return self.stack.pop()
def current(self):
return self.stack[-1]
def serialize_stack(nav):
# একটি সরল, JSON-উপযোগী প্রতিনিধিত্ব -- মূল list-এর একটি স্বাধীন কপি
return list(nav.stack)
def restore_stack(saved_screens):
# সংরক্ষিত তালিকা থেকে একটি সম্পূর্ণ নতুন, কার্যকর NavigationStack পুনর্গঠন
restored = NavigationStack(saved_screens[0])
for screen in saved_screens[1:]:
restored.push(screen)
return restored
# একটি বাস্তবসম্মত নেভিগেশন সেশন তৈরি করা হলো
nav = NavigationStack("Home")
nav.push("Profile")
nav.push("Settings")
nav.push("EditProfile")
print(f"অ্যাপ কিল হওয়ার আগে stack: {nav.stack}")
saved = serialize_stack(nav)
print(f"সংরক্ষিত (serialize_stack ফলাফল): {saved}")
print(f"এটি একই list অবজেক্ট কিনা: {saved is nav.stack}")
saved is nav.stack প্রিন্ট করে False — কারণ list(nav.stack) একটি
সম্পূর্ণ নতুন list তৈরি করে, শুধু একই কনটেন্ট কপি করে। এটি ইচ্ছাকৃত: nav এখনও লাইভ
থাকা অবস্থায় আরও push()/pop() হলে সেগুলো saved-কে প্রভাবিত করবে না —
saved ঠিক সেই মুহূর্তের একটি ফ্রোজেন স্ন্যাপশট।
৩ · সিমুলেটেড অ্যাপ-কিল + রিলঞ্চ — রিস্টোর করা স্ট্যাক যাচাই
এবার M1-এর background → killed ট্রানজিশনটি সিমুলেট করা হবে — মূল nav অবজেক্টটি
মুছে দেওয়া হবে (del nav), শুধু saved তালিকাটি "বেঁচে" থাকবে। তারপর
restore_stack() দিয়ে একটি নতুন স্ট্যাক পুনর্গঠন করে দুটি জিনিস যাচাই করা হবে — প্রথমত এর
বিষয়বস্তু আগের মতোই কিনা, দ্বিতীয়ত এটি থেকে pop() করলে সেই একই আচরণ হয় কিনা যা কিল হওয়ার আগে
হতো।
# --- M1 লাইফসাইকেল: background -> killed সিমুলেট করা হলো ---
# আসল nav অবজেক্টটি এখন মুছে গেলো -- শুধু 'saved' (ডিস্কে সংরক্ষিত ধরে নেওয়া তালিকা) বেঁচে আছে
del nav
# --- রিলঞ্চ: সংরক্ষিত তালিকা থেকে স্ট্যাক পুনর্গঠন করা হলো ---
restored_nav = restore_stack(saved)
print(f"রিস্টোর করা stack: {restored_nav.stack}")
# যাচাই ১: রিস্টোর করা স্ট্যাকের বিষয়বস্তু ও অর্ডার হুবহু সংরক্ষিত তালিকার সাথে মেলে কিনা
identical = restored_nav.stack == saved
print(f"যাচাই ১ -- restored_nav.stack == saved: {identical}")
assert identical
# যাচাই ২: রিস্টোর করা স্ট্যাক থেকে pop() করলে ঠিক সেই স্ক্রিনই ফেরত আসে যা কিল হওয়ার আগে আসতো
expected_next_pop = saved[-1] # কিল হওয়ার মুহূর্তে সবচেয়ে উপরের স্ক্রিন
actual_pop = restored_nav.pop()
print(f"\nকিল হওয়ার আগে যে স্ক্রিন pop() হতো: {expected_next_pop!r}")
print(f"রিস্টোরের পর pop() যা ফেরত দিলো: {actual_pop!r}")
assert actual_pop == expected_next_pop
print(f"\npop()-এর পরে রিস্টোর করা stack: {restored_nav.stack}")
assertই পাস করে — প্রমাণ করে রিস্টোর করা স্ট্যাক শুধু "দেখতে একই রকম" নয়, বরং
আচরণগতভাবেও ঠিক সেই লাইভ স্ট্যাকের মতোই কাজ করে যেটি কিল হওয়ার আগে ছিল। বাস্তব ডিভাইসে
saved তালিকাটি ডিস্কে (ফাইল বা key-value স্টোরেজে) লেখা থাকতো — এই স্যান্ডবক্স শুধু ইন-মেমোরি
ভ্যারিয়েবল দিয়ে সেই ধারণাটি সিমুলেট করছে; M8-এর (L32) key-value স্টোরেজ পাঠে প্রকৃত get()/
set() এপিআই কভার হবে, আর M7-এর (L31) স্টেট-রিস্টোরেশন পাঠে ঠিক এই একই প্যাটার্ন ফর্ম-ডেটার মতো
অন্য ধরনের স্টেটেও প্রয়োগ হবে।
একটি নেভিগেশন স্ট্যাক ইন-মেমোরি ডেটা — killed ট্রানজিশনে এটি স্বয়ংক্রিয়ভাবে বেঁচে থাকে না।
serialize_stack() এটিকে একটি স্বাধীন, সংরক্ষণযোগ্য তালিকায় রূপান্তর করে;
restore_stack() সেই তালিকা থেকে একটি নতুন, সমানভাবে কার্যকর স্ট্যাক পুনর্গঠন করে —
push()/pop() এর মাধ্যমেই, কোনো শর্টকাট ছাড়াই। equality check ও আচরণগত পরীক্ষা
দুটোই একসাথে নিশ্চিত করে রিস্টোরেশন সত্যিই নির্ভরযোগ্য।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
serialize_stack() কেন list(nav.stack) (একটি নতুন কপি) রিটার্ন করে, শুধু
nav.stack রিটার্ন করে না?
শুধু nav.stack রিটার্ন করলে saved আসল list অবজেক্টেরই আরেকটি নাম
হতো (একই মেমোরি রেফারেন্স) — তখন nav এখনও লাইভ থাকা অবস্থায় কেউ যদি আরও
push()/pop() করে, সেই পরিবর্তন saved-এও দেখা যেত, যদিও
saved-এর উদ্দেশ্য ছিল একটি নির্দিষ্ট মুহূর্তের স্ন্যাপশট ধরে রাখা। list(nav.stack)
একটি সম্পূর্ণ স্বাধীন কপি তৈরি করে, তাই স্ন্যাপশটটি সত্যিই "ফ্রোজেন" থাকে।
প্র ০২
restore_stack() সরাসরি restored.stack = saved_screens না করে
NavigationStack(saved_screens[0]) দিয়ে শুরু করে বাকিগুলো এক এক করে push()
করে কেন?
দুটো কারণ। প্রথমত, সরাসরি restored.stack = saved_screens করলে আবারও একই অ্যালিয়াসিং
সমস্যা হতো — restored.stack আর saved একই list অবজেক্ট হয়ে যেত।
দ্বিতীয়ত, প্রতিটি স্ক্রিন push() মেথডের মধ্য দিয়ে যোগ করা মানে রিস্টোরেশন ঠিক সেই একই,
ভ্যালিডেটেড পথ ব্যবহার করে যা স্বাভাবিক নেভিগেশনও ব্যবহার করে — ভবিষ্যতে push()-এ কোনো
অতিরিক্ত লজিক (যেমন সর্বোচ্চ গভীরতা চেক) যোগ হলে, রিস্টোরেশনও স্বয়ংক্রিয়ভাবে সেই একই নিয়ম মেনে চলবে।
প্র ০৩
দ্বিতীয় কোড সেলে equality check restored_nav.stack == saved-এর সাথে তুলনা করা হয়েছে,
মূল nav.stack-এর সাথে নয় কেন?
কারণ del nav-এর পর মূল nav অবজেক্টটি আর অস্তিত্বেই নেই — ঠিক যেমন বাস্তবে
একটি killed অ্যাপ প্রসেসের মেমোরি সম্পূর্ণ মুছে যায়। saved তালিকাটিই একমাত্র
জিনিস যা "বেঁচে" ছিল, তাই এটিই একমাত্র বাস্তবসম্মত তুলনার ভিত্তি — ঠিক যেমন বাস্তব অ্যাপকেও প্রমাণ করতে
হয় তার রিস্টোরেশন লজিক ডিস্কে যা লেখা ছিল তার সাথে মেলে, কোনো "আগের মেমোরি" এর সাথে নয় (সেটি আর নেই)।
অনুশীলন
-
চিন্তা করুন: প্রথম কোড সেলে
saved = serialize_stack(nav)লাইনের ঠিক পরে (কিন্তুdel nav-এর আগে) যদি আরেকটিnav.push("Notifications")কল করা হতো, তাহলে সেই পরিবর্তন কিsavedতালিকাতেও দেখা যেত? কেন বা কেন নয়?না, দেখা যেত না।
savedইতিমধ্যেইlist(nav.stack)দিয়ে একটি স্বাধীন কপি হিসেবে তৈরি হয়ে গিয়েছিল সেইpush()কল হওয়ার আগেই — তাইnav.stack-এ পরে যা যোগ হোক না কেন,savedতার সেই পুরনো, ফ্রোজেন অবস্থাতেই থাকবে (['Home', 'Profile', 'Settings', 'EditProfile'])। এই "নতুন"Notificationsস্ক্রিনটি সংরক্ষিত ধরতে হলেserialize_stack(nav)আবার কল করেsaved-কে নতুন করে বসাতে হতো। -
পরীক্ষা করুন: দ্বিতীয় কোড সেলের একদম শেষে দুটো নতুন লাইন যোগ করুন —
restored_nav.pop()আরও দুইবার কল করুন (মোট স্ট্যাক রুট"Home"-এ পৌঁছে যাবে), তারপর তৃতীয়বারrestored_nav.pop()কল করে ফলাফল প্রিন্ট করুন — এটি কি রুট স্ক্রিন সরিয়ে দেয়, নাকি L24-এর মতোই প্রত্যাখ্যান করে?দুইবার আরও
pop()করলে স্ট্যাক['Home']-এ নেমে আসবে (Settings আর Profile সরে যাবে)। তৃতীয়বারpop()কল করলে এটি রুট স্ক্রিনে পৌঁছে গেছে বলেlen(self.stack) <= 1চেক অনুযায়ীNoneফেরত দেবে এবং স্ট্যাক অপরিবর্তিত থাকবে — কারণrestore_stack()একটি সম্পূর্ণ স্বাভাবিকNavigationStackইনস্ট্যান্স তৈরি করে, তাই এরpop()মেথড ঠিক L24-এর মূল ক্লাসের মতোই আচরণ করে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ অ্যাপ লাইফসাইকেল, মোবাইল UI/UX, MVVM/MVI আর্কিটেকচার, নেভিগেশন, স্টেট ম্যানেজমেন্ট, লোকাল স্টোরেজ, ডিভাইস ফিচার ও ডিপ্লয়মেন্ট — সবগুলো মডিউল একসাথে দেখুন।
-
পুনরায় দেখুন: স্ট্যাক-ভিত্তিক নেভিগেশন L24
NavigationStackক্লাসের মূল ভিত্তি —push/pop/current— এই পাঠের সিরিয়ালাইজেশন ও রিস্টোরেশন লজিকের বুনিয়াদ। - সব 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 — সব এক জায়গায়।