এই পর্বে যা শিখবেন
- সাধারণ ভাষায় লেখা একটি সমস্যার বিবরণ থেকে সম্ভাব্য class আর method বের করা
- CRC কার্ড দিয়ে কে কী দায়িত্ব নেবে আর কে কার সাহায্য নেবে, সেটা ঠিক করা
- “৫০০ টাকা পে করুন” — একটি অনুরোধ কীভাবে object থেকে object-এ বার্তা হয়ে ঘোরে
- নকশাকে প্রথম ছোট্ট Java class-এ রূপ দেওয়া, আর pseudocode থেকে Java method লিখে dry run করা
- AI-এর দেওয়া domain model যাচাই করার চেকলিস্ট
১ · কোড লেখার আগে ভাবা — computational thinking-এর ঝটপট স্মরণ
আগের পর্বে দেখেছেন, Java-র সবচেয়ে বড় জায়গা হলো বড়, দীর্ঘদিন চলা সফটওয়্যার — ব্যাংকের সিস্টেম, Android অ্যাপ, সরকারি পোর্টাল। এমন সফটওয়্যারে সবচেয়ে কঠিন কাজ টাইপ করা নয়, ভাবা: কী বানাব, কোন অংশ কী করবে, পরে বদলাতে হলে কোথায় হাত দেব। এই পর্বে প্রায় কোনো ব্যাকরণ নেই। Java-র ব্যাকরণ শুরু হবে পর্ব ০৪ থেকে, আর class লেখার নিয়ম বিস্তারিত পাবেন পর্ব ১৫-এ। আজকের লক্ষ্য একটি অভ্যাস তৈরি করা।
যেকোনো প্রোগ্রামের আগে কয়েকটি চেনা কৌশল কাজে লাগে। একসাথে এদের বলা হয় computational thinking. Python কোর্সের সমস্যা সমাধানের পর্বে এগুলো বিস্তারিত আছে; এখানে শুধু মনে করিয়ে দিই, একটি রাইড-শেয়ারিং অ্যাপের উদাহরণে:
🧩 Decomposition
Decomposition মানে বড় সমস্যাকে ছোট টুকরোয় ভাঙা। “রাইড অ্যাপ বানাও” একবারে হয় না; রাইড চাওয়া, ড্রাইভার খোঁজা, ভাড়া হিসাব, পেমেন্ট, রেটিং — আলাদা টুকরো।
🔁 Pattern
বারবার আসা মিল খোঁজা। মোটরবাইক, গাড়ি, সিএনজি — সব রাইডেই “দূরত্ব × রেট + ভিত্তি ভাড়া” ধরনের হিসাব।
🎯 Abstraction
Abstraction মানে অদরকারি খুঁটিনাটি বাদ দেওয়া। ভাড়া হিসাবে ড্রাইভারের গাড়ির রং লাগে না।
📋 Algorithm
Algorithm মানে নির্ভুল ক্রমে লেখা ধাপ — আগে দূরত্ব মাপা, তারপর ভাড়া হিসাব, তারপর রাতের অতিরিক্ত চার্জ।
এই চারটি সব ভাষায় একই। কিন্তু decomposition কীভাবে করবেন, সেখানে ভাষার চরিত্র আলাদা হয়ে যায়। C-এর মতো ভাষায় ভাঙা হয় সাধারণত কাজ ধরে: “প্রোগ্রাম কোন কোন ধাপে চলবে?” উত্তর হয় কিছু ফাংশন আর পাশে আলাদা করে রাখা ডেটা। Java আপনাকে আরেকটি প্রশ্ন দিয়ে শুরু করতে শেখায়:
প্রত্যেকে কী জানে, কী করে, আর কার সাথে কথা বলে?”
রাইড অ্যাপে আছেন রাইডার, ড্রাইভার, আর একটি ট্রিপ। মোবাইল ওয়ালেটে আছে ওয়ালেট, লেনদেন, এজেন্ট। একটি স্কুলে আছে ছাত্র, বিষয় (course), ফলাফল। প্রত্যেকে একেকটি চরিত্র। প্রোগ্রাম মানে এই চরিত্রদের মধ্যে কাজ ভাগ করে দেওয়া, আর তাদের একে অপরকে অনুরোধ পাঠাতে দেওয়া।
Python বা C++-এ আপনি চাইলে class ছাড়াই পুরো প্রোগ্রাম লিখতে পারেন। Java-তে প্রতিটি method কোনো না কোনো class-এর ভেতরে থাকে — এমনকি প্রোগ্রাম শুরুর main method-ও। তাই Java শিখতে গেলে “কোন কোড কোন class-এর ঘরে যাবে” প্রশ্নটা প্রথম দিন থেকেই আসে। আজ সেই প্রশ্নের উত্তর দেওয়ার কৌশল শিখব।
এই চিন্তার শুরু ১৯৬০-এর দশকে নরওয়েতে। অসলোর Norwegian Computing Center-এ Ole-Johan Dahl ও Kristen Nygaard জাহাজঘাট বা কারখানার মতো ব্যবস্থার simulation লিখতে গিয়ে Simula ভাষা তৈরি করেন। Simula 67-এ প্রথম এল class, object আর subclass-এর ধারণা। সেখান থেকে ধারণাটি গেছে Smalltalk আর C++-এ, আর ১৯৯০-এর দশকে Sun Microsystems-এ James Gosling-এর দল এই ঐতিহ্য নিয়েই Java বানায়।
২ · Object মানে কী — যে কিছু জানে আর কিছু করতে পারে
আপনার ফোনের মোবাইল ওয়ালেটটা ভাবুন (bKash বা Nagad-এর মতো যেকোনো একটি)। এর দুটি দিক আছে:
- সে কী জানে — মালিকের নাম, ফোন নম্বর, বর্তমান ব্যালান্স, আজ কত টাকা পাঠানো হয়েছে। এগুলো তার state বা অবস্থা। অবস্থা সময়ের সাথে বদলায়: সকালে ৳১,২০০, দুপুরে পেমেন্টের পর ৳৭০০.
- সে কী করতে পারে — টাকা জমা নেওয়া, পে করা, পাঠানো, ব্যালান্স জানানো। এগুলো তার behaviour বা আচরণ।
এই দুটিকে একসাথে বাঁধলে পাওয়া যায় একটি object. আর “ওয়ালেট” জিনিসটা সাধারণভাবে কেমন হয় — কী কী তথ্য রাখে, কী কী কাজ করে — সেই নকশাকে বলে class. এক class থেকে অনেক object: রিনার ওয়ালেট, করিমের ওয়ালেট, আপনার ওয়ালেট। নকশা এক, কিন্তু প্রত্যেকের ব্যালান্স আলাদা।
Java-র ভাষায় এই দুই দিকের নামও আছে। Object যা জানে, তা রাখা হয় field-এ (class-এর ভেতরের ভ্যারিয়েবল)। Object যা করতে পারে, তা লেখা হয় method হিসেবে (class-এর ভেতরের ফাংশন)। এই সম্পর্কটা মনে রাখুন — পুরো কোর্সে বারবার আসবে।
তিনটি বাস্তব দুনিয়া, একই প্রশ্ন
| Object | কী জানে (state → field) | কী করে (behaviour → method) |
|---|---|---|
| Wallet (ওয়ালেট) | মালিক, ব্যালান্স, আজকের মোট লেনদেন | টাকা জমা নেয়, পে করে, ব্যালান্স জানায় |
| Driver (ড্রাইভার) | নাম, গাড়ির নম্বর, এখন ফাঁকা কি না, রেটিং | রাইড গ্রহণ করে, ট্রিপ শুরু ও শেষ করে |
| Trip (ট্রিপ) | রাইডার, ড্রাইভার, পিকআপ, গন্তব্য, দূরত্ব, অবস্থা | শুরু হয়, শেষ হয়, নিজের ভাড়া হিসাব করে |
| Student (ছাত্র) | নাম, রোল, কোন কোন বিষয় নিয়েছে | বিষয়ে ভর্তি হয়, নিজের ফলাফল জানায় |
| Result (ফলাফল) | কোন ছাত্র, কোন বিষয়, প্রাপ্ত নম্বর | গ্রেড হিসাব করে, পাস কি না বলে |
খেয়াল করুন, Trip কোনো মানুষ বা হাতে ধরা জিনিস নয় — এটা একটি ঘটনা। তবু তার নিজের তথ্য আছে, নিজের নিয়ম আছে (শুরু না হলে শেষ হতে পারে না)। Object মানে শুধু “বাস্তব জিনিস” নয়; যেকোনো ধারণা, যার নিজের তথ্য আর দায়িত্ব আছে, সেটাই object হতে পারে।
একটি ভালো রেস্তোরাঁর কথা ভাবুন। ওয়েটার অর্ডার নেন, বাবুর্চি রান্না করেন, ক্যাশিয়ার টাকা সামলান। প্রত্যেকে নিজের কাজ জানেন, নিজের জিনিস সামলান। ওয়েটার রান্নাঘরে ঢুকে নিজে রান্না করেন না — বাবুর্চিকে অর্ডার পাঠান। ক্যাশবাক্সে ক্যাশিয়ার ছাড়া কেউ হাত দেন না। ভালো object-ভিত্তিক প্রোগ্রাম ঠিক এভাবে চলে: প্রত্যেকে নিজের ঘর সামলায়, অন্যকে অনুরোধ পাঠায়।
Python আর C++-এ class ঐচ্ছিক — শুধু ফাংশন দিয়েও পুরো প্রোগ্রাম লেখা যায়। Java-তে তা হয় না: সবকিছু class-এর ভেতরে, আর “শুধু ফাংশন” লিখতে চাইলে সেটা হয় কোনো class-এর static method (যেমন Math.max). ভাবনার দিকটা একই; C++ কোর্সের বস্তু দিয়ে ভাবা পর্বে procedural বনাম object-এর তুলনাটা বিস্তারিত আছে। এগুলোর কোনোটি না জানলে এই বাক্সটি বাদ দিয়ে এগোতে পারেন।
৩ · বিবরণ থেকে class — রাইড অ্যাপে বিশেষ্য-ক্রিয়া বিশ্লেষণ
বাস্তবে কেউ আপনাকে “এই পাঁচটা class বানাও” বলে দেবেন না। আপনি পাবেন সাধারণ ভাষায় লেখা কয়েক লাইনের বিবরণ। ধরুন ঢাকার একটি স্টার্টআপ “চলো” নামে একটি কাল্পনিক রাইড অ্যাপ বানাতে চায়। বিবরণ থেকে নকশায় যাওয়ার একটি পুরোনো, কার্যকর কৌশল হলো noun/verb analysis — বিশেষ্য দাগান, ক্রিয়া দাগান। নিচের ডায়াগ্রামে “পরে” চেপে ধাপে ধাপে দেখুন:
রাইডার অ্যাপে পিকআপ আর গন্তব্য দিয়ে রাইড চান। কাছের একজন ফাঁকা ড্রাইভার অনুরোধটি গ্রহণ করেন। ট্রিপ শেষে দূরত্ব অনুযায়ী ভাড়া হিসাব হয়। রাইডার ক্যাশ বা ওয়ালেটে ভাড়া দেন। ট্রিপ শেষে দুজন দুজনকে রেটিং দেন। একজন ড্রাইভার একসাথে একটির বেশি ট্রিপ নিতে পারেন না।
রাইডার অ্যাপে পিকআপ আর গন্তব্য দিয়ে রাইড চান। কাছের একজন ফাঁকা ড্রাইভার অনুরোধটি গ্রহণ করেন। ট্রিপ শেষে দূরত্ব অনুযায়ী ভাড়া হিসাব হয়। রাইডার ক্যাশ বা ওয়ালেটে ভাড়া দেন। ট্রিপ শেষে দুজন দুজনকে রেটিং দেন। একজন ড্রাইভার একসাথে একটির বেশি ট্রিপ নিতে পারেন না।
রাইডার অ্যাপে পিকআপ আর গন্তব্য দিয়ে রাইড চান। কাছের একজন ফাঁকা ড্রাইভার অনুরোধটি গ্রহণ করেন। ট্রিপ শেষে দূরত্ব অনুযায়ী ভাড়া হিসাব হয়। রাইডার ক্যাশ বা ওয়ালেটে ভাড়া দেন। ট্রিপ শেষে দুজন দুজনকে রেটিং দেন। একজন ড্রাইভার একসাথে একটির বেশি ট্রিপ নিতে পারেন না। <- নিয়ম!
Rider রাইডার -> class (নাম, ফোন, রেটিং) Driver ড্রাইভার -> class (ফাঁকা কি না জানে) Trip ট্রিপ/রাইড -> class (রাইড = ট্রিপ, একই জিনিস) Location পিকআপ/গন্তব্য -> ছোট মান (অক্ষাংশ, দ্রাঘিমাংশ) Rating রেটিং -> ছোট মান (১-৫ তারা, কে দিল) দূরত্ব -> Trip-এর ডেটা ভাড়া -> Trip হিসাব করে, আলাদা class নয় (এখনই) অ্যাপ -> পুরো সিস্টেম নিজেই, class নয় ক্যাশ/ওয়ালেট -> পেমেন্টের ধরন (পরে ভাবা যাবে)
- শুরু — সাধারণ ভাষার বিবরণ। প্রোডাক্ট ম্যানেজার কোড বোঝেন না, ব্যবসা বোঝেন। এই ছয় লাইনই আপনার কাঁচামাল। এখানে “ফাংশন” বা “ভ্যারিয়েবল” শব্দ নেই — আছে মানুষ, জিনিস, ঘটনা আর কাজ।
- বিশেষ্য দাগান। প্রতিটি নাম-শব্দ — রাইডার, ড্রাইভার, ট্রিপ, রাইড, পিকআপ, গন্তব্য, দূরত্ব, ভাড়া, রেটিং, ওয়ালেট — একেকটি সম্ভাব্য class. এখনই বাদ দেবেন না; আগে সবগুলো তুলে আনুন।
- ক্রিয়া দাগান। কাজের শব্দ — চাওয়া, গ্রহণ করা, শেষ করা, হিসাব করা, দেওয়া — হবে কারও না কারও method. আর শেষ লাইনটা কাজ নয়, একটা নিয়ম: “একসাথে একটির বেশি ট্রিপ নয়।” এমন “কখনো না” লাইন খুব মূল্যবান; এরাই পরে invariant হয়।
- তালিকা ছাঁকুন। প্রতিটি বিশেষ্যকে জিজ্ঞেস করুন: “এর কি নিজের তথ্য আর নিজের কাজ আছে?” Rider, Driver, Trip-এর আছে। “রাইড” আর “ট্রিপ” একই জিনিসের দুই নাম — একটি নাম বেছে নিন। দূরত্ব শুধু Trip-এর একটি তথ্য। পিকআপ আর রেটিং ছোট মান — পরে record হতে পারে (পর্ব ২৫)।
- প্রথম খসড়া। তিনটি মূল class, প্রতিটি ক্রিয়া একজন মালিক পেয়েছে। সবচেয়ে মজার ব্যাপার: “রাইড চাওয়া” একটি ক্রিয়া ছিল, কিন্তু তার ফল — Trip — নিজেই একটি class হয়ে গেছে, কারণ তার নিজের তথ্য (দূরত্ব, অবস্থা) আর নিজের নিয়ম আছে। Trip এখন নকশার কেন্দ্র। এটা খসড়া মাত্র; পরের অংশে যাচাই করব।
বিশেষ্য-ক্রিয়ার কৌশল একটি শুরুর হাতিয়ার, যান্ত্রিক নিয়ম নয়। সব বিশেষ্য class হয় না (“অ্যাপ”, “দূরত্ব”), আবার কিছু গুরুত্বপূর্ণ class বিবরণে স্পষ্ট লেখা থাকে না — যেমন কাছের ফাঁকা ড্রাইভার খুঁজে বের করার কাজটা কার? রাইডারের নয়, ড্রাইভারেরও নয়। হয়তো একটি আলাদা Dispatcher লাগবে। বিবরণ পড়ে প্রশ্ন করুন, প্রোডাক্ট ম্যানেজারকে জিজ্ঞেস করুন।
একই কৌশল, অন্য দুনিয়া — লাইব্রেরি
একটি পাড়ার লাইব্রেরির বিবরণ: “সদস্য একসাথে সর্বোচ্চ তিনটি বই ধার নিতে পারেন। ১৪ দিনের মধ্যে ফেরত না দিলে প্রতিদিন ৫ টাকা জরিমানা।” বিশেষ্য: সদস্য, বই, দিন, জরিমানা। ক্রিয়া: ধার নেওয়া, ফেরত দেওয়া। এখানেও একটি লুকানো class আছে — Loan (ধার)। “ধার নেওয়া” ক্রিয়ার ফল হিসেবে একটি রেকর্ড তৈরি হয়, যার নিজের তথ্য আছে (কোন বই, কে, কবে, কবে ফেরত দিতে হবে) আর নিজের কাজ আছে (জরিমানা হিসাব)। ক্রিয়া থেকে বিশেষ্য জন্ম নেওয়া — অভিজ্ঞ ডিজাইনাররা এটা প্রায়ই খোঁজেন।
৪ · দায়িত্ব আর সহযোগী — ওয়ালেটের CRC কার্ড
Class-এর তালিকা পাওয়া গেল। এবার আসল প্রশ্ন: কে কোন কাজের দায়িত্ব নেবে? ভুল জায়গায় দায়িত্ব দিলে প্রোগ্রাম চলবে ঠিকই, কিন্তু পরে বদলাতে গেলে সব জট পাকিয়ে যাবে। এই প্রশ্নের জন্য সবচেয়ে সস্তা আর কার্যকর হাতিয়ার হলো CRC card. ১৯৮৯ সালে Ward Cunningham ও Kent Beck একটি প্রবন্ধে কৌশলটি পরিচিত করেন। একটি সাধারণ ইনডেক্স কার্ডে তিনটি অংশ:
- C — Class: ওপরে class-এর নাম।
- R — Responsibilities: বাঁ দিকে, সে কী জানে আর কী করে। প্রতিটি responsibility লিখুন ছোট ক্রিয়াপদে।
- C — Collaborators: ডান দিকে, দায়িত্ব পালনে সে কাদের সাহায্য নেয়, অর্থাৎ কাদের বার্তা পাঠায়। এরা তার collaborator.
কার্ড ছোট — এটাই শক্তি। একটি কার্ডে দায়িত্ব না ধরলে বুঝবেন class-টা বেশি কাজ নিচ্ছে। এবার একটি মোবাইল ওয়ালেটের জন্য CRC সেশন চালাই। দৃশ্য: “রিনা মোড়ের এজেন্টের কাছে ৳১,০০০ cash in করলেন, তারপর একটি দোকানে ৳৫০০ পেমেন্ট করলেন।”
- প্রথম কার্ড — Wallet. দৃশ্যে সবচেয়ে কেন্দ্রীয় জিনিস টাকা, আর টাকা থাকে ওয়ালেটে। দায়িত্ব: ব্যালান্স জানা, টাকা বাড়ানো-কমানো, আর সবচেয়ে বড় দায়িত্ব — ব্যালান্স কখনো ঋণাত্মক হতে না দেওয়া। দায়িত্ব লিখুন কাজ হিসেবে, শুধু ডেটার তালিকা হিসেবে নয়।
- দ্বিতীয় কার্ড — Agent. এজেন্টের নিজেরও একটি ইলেকট্রনিক টাকার মজুত থাকে। রিনা ৳১,০০০ নগদ দিলে এজেন্টের মজুত কমে, রিনার ওয়ালেটে বাড়ে। তাই Agent-এর collaborator হলো Wallet আর Transaction.
- তৃতীয় কার্ড — Transaction. প্রতিটি টাকা নড়াচড়ার একটি রেকর্ড চাই — কে, কাকে, কত, কখন। আরও বড় দায়িত্ব: এক দিক থেকে কাটা আর অন্য দিকে জমা — দুটোই হবে, নয়তো কোনোটাই না। Wallet-এর কার্ডেও এখন collaborator হিসেবে Transaction লেখা হলো।
- দৃশ্য অভিনয় — cash in. তিনজন তিনটি কার্ড হাতে নিন। একজন বলুন “রিনা ১০০০ cash in করলেন।” Agent-এর কার্ড-ধারী বলবেন, “আমি একটি Transaction বানালাম।” Transaction বলবে, “এজেন্টের মজুত থেকে কাটলাম, রিনার Wallet-কে credit বার্তা পাঠালাম।” প্রতিটি ধাপে কারও না কারও কার্ডে দায়িত্বটি লেখা আছে — দৃশ্যটা চলল।
- ফাঁক ধরা পড়ল — “দৈনিক সীমা কে দেখবে?” দোকানে পেমেন্টের দৃশ্যে কেউ জিজ্ঞেস করলেন, “দিনে কত টাকা খরচ করা যাবে, সেই সীমা কে মানবে?” কারও কার্ডে লেখা নেই! আলোচনা করে ঠিক হলো: সীমা নির্ভর করে ওয়ালেটের আজকের মোট খরচের ওপর, আর সেই তথ্য Wallet-ই জানে — তাই দায়িত্বটা Wallet-এর। কোড লেখার আগেই নকশার একটি ফাঁক ধরা পড়ল।
- শেষ পরীক্ষা — কোনো কার্ড কি বেশি ভারী? প্রতিটি কার্ডের দায়িত্ব এক বাক্যে বলুন। Wallet: “টাকার হিসাব আর নিয়ম।” Agent: “নগদ আর ইলেকট্রনিক টাকার বিনিময়।” Transaction: “একটি লেনদেন নির্ভুলভাবে শেষ করা।” তিনটিই এক বাক্যে বলা গেল — ভালো লক্ষণ। কেউ যদি Wallet-এ “SMS পাঠানো” আর “অফার দেখানো”-ও লিখতে চান, সেগুলো আলাদা কার্ডে যাওয়া উচিত।
শেষ ধাপের পরীক্ষাটা গুরুত্বপূর্ণ। একটি class-এর কাজগুলো একটি লক্ষ্যে কতটা জড়ো, তাকে বলে cohesion. এক বাক্যে দায়িত্ব বলা গেলে cohesion বেশি — যা আমরা চাই। একটি class যদি প্রায় সবকিছু করে — ব্যালান্স, SMS, অফার, রিপোর্ট, লগইন — তাকে বলে god class. উল্টো দিকে, যে class শুধু ডেটা রাখে আর সব সিদ্ধান্ত বাইরে অন্য কেউ নেয়, তাকে বলে anemic class (রক্তশূন্য class). দুটোই খারাপ নকশার লক্ষণ, আর AI-এর লেখা কোডে দুটোই প্রায়ই দেখা যায় (অংশ ৯)।
৫ · বার্তা পাঠানো — “৫০০ টাকা পে করুন” কীভাবে ঘোরে
নকশা হলো, কিন্তু কাজটা আসলে হয় কীভাবে? Object-দের জগতে কেউ কারও ডেটায় সরাসরি হাত দেয় না — একে অপরকে বার্তা পাঠায়: “৫০০ টাকা কাটো”, “এই টাকা জমা নাও।” গ্রহণকারী নিজেই ঠিক করে কীভাবে সাড়া দেবে, এমনকি “না” বলতেও পারে। একে বলে message passing. Java-তে বার্তা পাঠানো মানে একটি object-এর method ডাকা — rinaWallet.pay(500) — যার নিয়ম পর্ব ১৫-এ শিখবেন।
রিনার ওয়ালেটে আছে ৳১,২০০. তিনি “করিম স্টোর”-এ ৳৫০০ পেমেন্ট করছেন, তারপর ভুল করে আরেকবার ৳৯০০ পেমেন্টের চেষ্টা করছেন। বাঁ দিকে বার্তার তালিকা (কোড নয়), ডান দিকে প্রতিটি object-এর অবস্থা। “পরে” চেপে দেখুন কোন বার্তায় কার অবস্থা বদলায়:
1app -> rina : pay(shop, 500)2rina -> rina : canSpend(500)?3rina -> tx : new Transaction(rina, shop, 500)4tx -> rina : debit(500)5tx -> shop : credit(500)6tx -> app : SUCCESS TX10017app -> rina : pay(shop, 900)8rina -> app : REJECTED (balance 700)
| rina.balance | |
| shop.balance | |
| tx.status | |
| rina.spentToday |
- অ্যাপ অনুরোধ পাঠাল। রিনা পিন দিয়ে “Payment” চাপলেন। অ্যাপের পর্দা রিনার Wallet object-কে বার্তা পাঠাল: “করিম স্টোরকে ৫০০ পে করো।” অ্যাপ নিজে ব্যালান্স থেকে কিছু বিয়োগ করছে না — সে শুধু অনুরোধ করছে।
- Wallet নিজের নিয়ম মিলিয়ে দেখল। ব্যালান্স ১২০০, চাওয়া ৫০০ — যথেষ্ট আছে। আজকের খরচ ০, দৈনিক সীমার নিচে। নিয়ম পাহারা দেওয়ার দায়িত্ব Wallet-এর (CRC কার্ডে যেমন লিখেছিলাম), অ্যাপের নয়।
- একটি Transaction জন্ম নিল। কে, কাকে, কত — এই তথ্য নিয়ে নতুন একটি লেনদেন object তৈরি হলো, অবস্থা PENDING. Java-তে নতুন object বানাতে
newলেখা হয় (পর্ব ১৫)। এখন থেকে কাজটা ঠিকমতো শেষ করার দায়িত্ব এই লেনদেনের। - রিনার টাকা কাটা হলো। Transaction রিনার Wallet-কে বার্তা পাঠাল:
debit(500). ব্যালান্স ৭০০, আজকের খরচ ৫০০. খেয়াল করুন — ব্যালান্স বদলেছে Wallet নিজেই; Transaction শুধু অনুরোধ করেছে। - দোকানের টাকা জমা হলো। দোকানের Wallet ৩০০০ থেকে ৩৫০০. একটি গভীর প্রশ্ন: ধাপ ৪ সফল কিন্তু ধাপ ৫ ব্যর্থ হলে? টাকা হাওয়া হয়ে যাবে! তাই আসল ব্যবস্থায় Transaction-কে দায়িত্ব নিতে হয় “দুটোই, নয়তো কোনোটাই না” — ব্যর্থ হলে রিনার টাকা ফেরত। এই দায়িত্ব কার, সেটা CRC সেশনেই ঠিক করেছিলাম।
- ফল জানানো হলো। লেনদেন DONE, রসিদ নম্বর TX1001, পর্দায় “Paid 500.” পুরো গল্পে কেউ অন্যের ডেটায় সরাসরি হাত দেয়নি — শুধু বার্তা গেছে। প্রতিটি object নিজের অবস্থার একমাত্র মালিক।
- Edge case — আবার ৯০০ পে করার চেষ্টা। রিনা ভুল করে আরেকটি ৯০০ টাকার পেমেন্ট চাপলেন। একই বার্তা, শুধু অঙ্ক আলাদা। এমন সীমার বাইরের পরিস্থিতিকে বলে edge case — নকশা ঠিক কি না, তা এগুলোতেই ধরা পড়ে।
- Wallet “না” বলল। ৭০০ টাকা থেকে ৯০০ দেওয়া যায় না। Wallet নিজের নিয়মে অনুরোধ ফিরিয়ে দিল — কোনো Transaction তৈরি হলো না, কোনো ব্যালান্স বদলাল না। নিয়মটা এক জায়গায় থাকায় অ্যাপ, ওয়েবসাইট বা এজেন্টের যন্ত্র — যেখান থেকেই অনুরোধ আসুক, একই সুরক্ষা।
যার ডেটা, নিয়ম রক্ষার দায়িত্বও তার।
একটি পুরোনো নকশা-নীতি এটাকে বলে “Tell, don’t ask”: object-কে তার ডেটা জিজ্ঞেস করে বাইরে সিদ্ধান্ত নেবেন না (“ব্যালান্স কত? ঠিক আছে, আমিই বিয়োগ করে দিচ্ছি”), বরং কাজটা করতে বলুন (“৫০০ পে করো”), সিদ্ধান্ত সে নিক।
৬ · Abstraction, encapsulation আর সম্পর্ক — সামনের পর্বগুলোর ঝলক
আপনি রিকশায় উঠে বলেন “ধানমন্ডি ২৭”. চালক কোন গলি দিয়ে যাবেন, কোথায় জ্যাম এড়াবেন — এসব জানার দরকার নেই। আপনার আর চালকের চুক্তি সহজ: গন্তব্য দেবেন, ভাড়া দেবেন, পৌঁছে যাবেন। Object-এর জগতেও তাই। রিনার Wallet-কে ব্যবহারকারী শুধু জানেন pay, balance — ভেতরে সীমা কীভাবে হিসাব হয়, ডেটা কোথায় রাখা, জানার দরকার নেই। দরকারি অংশ দেখানো আর বাকিটা আড়ালে রাখা — এটাই abstraction.
Abstraction-এর যমজ ভাই encapsulation: ডেটা আর সেই ডেটার নিয়ম একই class-এ রাখা, আর বাইরের কেউ যেন নিয়ম এড়িয়ে ডেটা বদলাতে না পারে। Java-তে এটা করা হয় private শব্দ দিয়ে — বিস্তারিত পর্ব ১৭-এ। আজ শুধু মনে রাখুন: ওয়ালেটের ব্যালান্স বাইরে থেকে সরাসরি লেখা যাওয়া উচিত নয়। অংশ ৭-এর কোডে দেখবেন, এই সুরক্ষা না থাকলে কী হয়।
has-a আর is-a — দুই ধরনের সম্পর্ক
Class পাওয়ার পর প্রশ্ন আসে, তারা একে অপরের সাথে কীভাবে যুক্ত। দুটি সম্পর্ক সবচেয়ে বেশি দেখা যায়:
has-a · “এর আছে”
Trip-এর একজন Driver আছেন। Student-এর অনেক Result আছে। একটি object আরেকটিকে নিজের ভেতরে রাখে বা চেনে — একে বলে has-a সম্পর্ক (composition). Java-তে এটা সাধারণত একটি field: Trip-এর ভেতরে একটি Driver.
সবচেয়ে বেশি ব্যবহৃতis-a · “এটি এক ধরনের”
MerchantWallet (দোকানের ওয়ালেট) এক ধরনের Wallet — সব নিয়ম একই, শুধু ফি আলাদা। একে বলে is-a সম্পর্ক, যা Java-তে লেখা হয় inheritance দিয়ে (পর্ব ১৯)।
সাবধানে, কম ব্যবহার করুনএকটি সহজ পরীক্ষা: বাক্যটা জোরে বলুন। “Trip is a Driver” — অর্থহীন। “Trip has a Driver” — ঠিক। “MerchantWallet is a Wallet” — ঠিক। সন্দেহ হলে has-a বেছে নিন; পেশাদার Java ডিজাইনে বহু বছর ধরে একটি পরামর্শ চলে আসছে — “inheritance-এর চেয়ে composition পছন্দ করুন।” কেন, সেটা পর্ব ১৯ আর design pattern-এর পর্ব ৪৮-এ দেখবেন।
৭ · ভাবনা থেকে Java — প্রথম ছোট্ট class
এতক্ষণ কাগজে-কলমে ভাবলাম। এবার দেখি সেই ভাবনা Java-তে দেখতে কেমন। নিচের কোডের প্রতিটি শব্দ এখনই বুঝতে হবে না — ব্যাকরণ শুরু হবে পর্ব ০৪ থেকে, আর class-এর নিয়ম পর্ব ১৫-এ। শুধু মন্তব্যগুলো (// দিয়ে শুরু লাইন) পড়ুন, আর মিলিয়ে দেখুন: state কোথায়, behaviour কোথায়। তারপর “চালান” চাপুন।
class Wallet {
String owner; // state: what the wallet knows
long balance;
boolean pay(long amount) { // behaviour: what it can do
if (amount <= 0 || amount > balance) return false; // the rule
balance -= amount;
return true;
}
}
class Main {
public static void main(String[] args) {
Wallet rina = new Wallet(); // one object from the class
rina.owner = "Rina";
rina.balance = 1200;
System.out.println("pay 500: " + rina.pay(500));
System.out.println("pay 900: " + rina.pay(900));
System.out.println(rina.owner + " has " + rina.balance);
}
}আউটপুট তিন লাইন: pay 500: true, pay 900: false, Rina has 700. অংশ ৫-এর বার্তার গল্পটাই এখানে, ছোট আকারে। class Wallet হলো নকশা: দুটি field (owner, balance) তার state, আর pay method তার behaviour. new Wallet() সেই নকশা থেকে একটি আসল object বানায়, আর rina.pay(500) হলো রিনার ওয়ালেটকে পাঠানো বার্তা। ৯০০-র অনুরোধে ওয়ালেট নিজের নিয়মে false দিয়েছে। আর main-ও একটি class-এর (Main) ভেতরে — Java-তে এর বাইরে কোড লেখার জায়গা নেই। (এই পাতায় কোড চালানোর জন্য class Main-এর আগে public লেখা হয়নি; নিজের প্রজেক্টে সাধারণত Main.java ফাইলে public class Main লেখা হয় — পর্ব ০৪-এ দেখবেন।)
কোডটিতে একটি ইচ্ছাকৃত দুর্বলতা আছে। main-এর ভেতরে কেউ চাইলে rina.balance = -5000; লিখে দিতে পারেন — compiler কোনো আপত্তি করবে না, কারণ field দুটি সবার জন্য খোলা। অর্থাৎ pay-এর নিয়ম এড়িয়ে যাওয়ার রাস্তা খোলা আছে। এই রাস্তা বন্ধ করার কৌশলই encapsulation — field-এর আগে private লিখে, আর object তৈরির সময় মান দিতে constructor ব্যবহার করে (পর্ব ১৬ ও ১৭)। আপাতত শুধু সমস্যাটা চিনে রাখুন।
CRC কার্ড থেকে Java কাঠামো
কার্ড আর কোডের মধ্যে সম্পর্কটা প্রায় সরাসরি:
| CRC কার্ডে | Java-তে | উদাহরণ |
|---|---|---|
| Class (নাম) | class ঘোষণা | class Wallet |
| “জানে” ধরনের দায়িত্ব | field | long balance |
| “করে” ধরনের দায়িত্ব | method | pay(amount) |
| Collaborator | অন্য class-এর object-কে field-এ রাখা বা method-এ পাওয়া, তারপর তার method ডাকা | tx.debit(500) |
| “কখনো না” নিয়ম | method-এর ভেতরের যাচাই (পরে private দিয়ে সুরক্ষিত) | amount > balance হলে প্রত্যাখ্যান |
৮ · অ্যালগরিদম, pseudocode আর dry run — যখন object লাগে না
Object নকশা ঠিক করে কে কাজটা করবে। কিন্তু কাজটা কীভাবে হবে — সেটা এখনো একটি অ্যালগরিদম। ধরুন “চলো” অ্যাপের ভাড়ার নিয়ম (সংখ্যাগুলো কাল্পনিক): ভিত্তি ভাড়া ৪০ টাকা, প্রতি কিলোমিটারে ২৫ টাকা; রাত হলে মোট ভাড়ার ২০% বেশি; দূরত্ব শূন্য বা তার কম হলে ভাড়া শূন্য। প্রথমে pseudocode — সাধারণ ভাষায় ধাপগুলো — তারপর পাশাপাশি Java:
| Pseudocode | Java |
|---|---|
| ভাড়া(কিমি, রাত কি না): | static int fare(int km, boolean night) { |
| যদি কিমি ≤ ০ → ০ ফেরত দাও | if (km <= 0) return 0; |
| মোট ← ৪০ + ২৫ × কিমি | int total = 40 + 25 * km; |
| যদি রাত → মোট ← মোট + মোটের ৫ ভাগের ১ ভাগ | if (night) total += total / 5; |
| মোট ফেরত দাও | return total; } |
লক্ষ করুন, প্রায় লাইনে লাইনে মিল। ভালো pseudocode থাকলে Java লেখা প্রায় অনুবাদের কাজ। এবার পুরো প্রোগ্রাম — কয়েকটি আলাদা ইনপুটে চালিয়ে দেখুন:
class Main {
// base 40 + 25 per km; night adds 20%; no distance, no fare
static int fare(int km, boolean night) {
if (km <= 0) return 0; // edge case first
int total = 40 + 25 * km;
if (night) total += total / 5; // 20% = one fifth
return total;
}
public static void main(String[] args) {
System.out.println("0 km : " + fare(0, false));
System.out.println("1 km : " + fare(1, false));
System.out.println("4 km : " + fare(4, false));
System.out.println("4 km, night : " + fare(4, true));
}
}আউটপুট: ০ কিমিতে 0, ১ কিমিতে 65, ৪ কিমিতে 140, আর রাতে ৪ কিমিতে 168. এখানে কোনো object বানানো হয়নি — fare একটি static method, যা কোনো object-এর অবস্থা ছাড়াই শুধু ইনপুট থেকে উত্তর বের করে। কিন্তু Java-র নিয়ম অনুযায়ী এটাও একটি class-এর (Main) ভেতরে থাকতে হয়েছে। ২০% হিসাবটা পূর্ণসংখ্যায় করা হয়েছে (total / 5), যাতে দশমিকের ঝামেলা না থাকে; ভাগফলের ভগ্নাংশ কেটে যায় — টাকার হিসাবে এটা জেনেশুনে নেওয়া সিদ্ধান্ত হওয়া উচিত।
হাতে চালিয়ে দেখা — dry run
কোড চালানোর আগে কাগজে লাইন ধরে ধরে চালিয়ে প্রতিটি ভ্যারিয়েবলের মান লিখে রাখাকে বলে dry run. পরীক্ষায় “আউটপুট কী হবে” প্রশ্নে আর AI-এর কোড যাচাইয়ে এটাই সবচেয়ে কাজের দক্ষতা। fare(4, true) ডাকলে কী হয়, ধাপে ধাপে দেখুন:
1static int fare(int km, boolean night) {2 if (km <= 0) return 0;3 int total = 40 + 25 * km;4 if (night) total += total / 5;5 return total;6}
| km | |
| night | |
| total |
- Method ডাকা হলো।
kmপেল ৪,nightপেলtrue.totalএখনো তৈরি হয়নি, তাই টেবিলে “—”। - Edge case যাচাই। ৪ কি শূন্য বা তার কম? না। তাই
return 0চলল না, পরের লাইনে যাওয়া হলো। এই লাইনটা না থাকলে শূন্য কিলোমিটারেও ৪০ টাকা ভাড়া উঠত — একটা চুপচাপ বাগ। - মূল হিসাব। ২৫ × ৪ = ১০০, তার সাথে ভিত্তি ৪০ —
totalহলো ১৪০. - রাতের চার্জ।
nightসত্য, তাই ১৪০ / ৫ = ২৮ যোগ হলো।totalএখন ১৬৮. খেয়াল করুন, বাগ ধরার জন্য এখানে প্রশ্ন করুন: ১৪৩ টাকা হলে ১৪৩ / ৫ = ২৮ (৬০ পয়সা কেটে গেল) — এটা কি ব্যবসার নিয়মের সাথে মেলে? - ফল ফেরত। Method ১৬৮ ফেরত দিল — কোড চালিয়ে যে আউটপুট পেয়েছিলেন, তার সাথে মিলে গেল। হাতে চালানো আর আসল চালানো মিললে আপনি কোডটা সত্যিই বুঝেছেন।
Edge case — কোথায় কোড ভাঙতে পারে
ভালো প্রোগ্রামার প্রথমেই সীমার পরিস্থিতিগুলো তালিকা করেন। ভাড়ার method আর ওয়ালেটের জন্য কয়েকটি:
- শূন্য আর ঋণাত্মক: ০ কিমি, −৩ কিমি (GPS-এর ভুলে আসতে পারে), ০ বা −৫০০ টাকার পেমেন্ট।
- ঠিক সীমায়: ব্যালান্স ঠিক ৫০০, পেমেন্ট ঠিক ৫০০ — হওয়া উচিত (তাই নিয়মে
>,>=নয়)। - বিশাল মান: কেউ ১০ লাখ কিমি লিখলে
int-এর গুণফল কি সীমা ছাড়াবে? (সংখ্যার সীমা পর্ব ০৬-এ।) - একই অনুরোধ দুবার: নেটওয়ার্ক ধীর বলে রিনা দুবার “Pay” চাপলেন — দুবার কাটা যাবে?
কখন object বাড়াবাড়ি
ভাড়ার হিসাবটা দেখুন: ইনপুট দুটি সংখ্যা, আউটপুট একটি সংখ্যা, কোনো মনে রাখার মতো অবস্থা নেই। এর জন্য FareCalculator class, তার object, তার FareCalculatorFactory — এসব বানানো বাড়াবাড়ি। Java-তে class লাগবেই, কিন্তু সেটা হতে পারে একটি সাধারণ ঘর, যার ভেতরে একটি static method. JDK নিজেই এভাবে লেখা: Math.max, Math.sqrt — কোনো Math object বানাতে হয় না।
নিজের অবস্থা আর নিয়ম আছে → object (Wallet, Trip). শুধু তথ্যের ছোট বান্ডিল → record (Location, Rating; পর্ব ২৫)। ইনপুট থেকে উত্তর, কোনো অবস্থা নেই → static method (ভাড়া, ভ্যাট হিসাব)। Java-তে সব কোড class-এ থাকে বলে সবকিছু object হতে হবে — এমন নয়। Function দিয়ে ভাবাও শিখুন; lambda আর Stream-এর পর্বে (পর্ব ৩৪–৩৫) এটা আরও কাজে লাগবে।
বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এই ধরনের প্রশ্ন প্রায়ই আসে:
- Class আর object-এর পার্থক্য কী? — Class হলো নকশা, object হলো সেই নকশা থেকে
newদিয়ে তৈরি আসল জিনিস, যার নিজের state আছে। - Object-এর state আর behaviour কী? — State হলো object যা জানে (field-এ রাখা মান), behaviour হলো object যা করতে পারে (method).
- OOP-এর মূল স্তম্ভ কয়টি? — সাধারণত চারটি বলা হয়: abstraction, encapsulation, inheritance, polymorphism.
- Java কি সম্পূর্ণ object-oriented? — না;
int,double-এর মতো primitive টাইপ object নয়, আরstaticmethod কোনো object ছাড়াই চলে। তবে সব কোড class-এর ভেতরে থাকে। - CRC কার্ড কী? — Class, Responsibility, Collaborators লেখা ছোট কার্ড, যা দিয়ে কোডের আগে দায়িত্ব ভাগ করা ও নকশা যাচাই করা হয়।
৯ · AI-এর যুগে — আগে domain model, তারপর সমালোচনা
AI-কে “একটি রাইড অ্যাপের Java কোড লিখে দাও” বললে কয়েক সেকেন্ডে কয়েকশো লাইন পাবেন। কিন্তু সেই কোডের নকশা ভালো কি না, যাচাই করবেন কীভাবে? সঠিক ক্রম হলো: আগে কোড নয়, আগে domain model চান — কোন কোন class, প্রত্যেকের দায়িত্ব কী, কে কার collaborator. সেটা যাচাই করুন, তারপর কোড।
AI-এর domain model-এ যে তিনটি ভুল প্রায়ই দেখা যায়
| ভুল | কীভাবে চিনবেন | কী করবেন |
|---|---|---|
| অতিরিক্ত class | ছোট সমস্যায় ১২–১৫টি class; RideManager, RideService, RideHelper, RideFactory — নামে আলাদা, কাজে প্রায় এক | প্রতিটি class-এর দায়িত্ব এক বাক্যে বলতে বলুন; বলা না গেলে বা দুটির বাক্য এক হলে মিলিয়ে দিন |
| God class | একটি AppManager বা RideService-এ ৩০টি method — রাইড, পেমেন্ট, রেটিং, SMS সব | CRC কার্ডে লিখুন; একটি কার্ডে না ধরলে দায়িত্ব ভাগ করুন |
| Anemic class | Wallet-এ শুধু field আর getter/setter; ব্যালান্সের নিয়ম অন্য কোনো “Service”-এ | নিয়মটা ডেটার মালিকের কাছে সরান: wallet.pay(500), wallet.setBalance(...) নয় |
AI-কে ডিজাইন চাইলে আগে নিজের খসড়া দিন আর সীমা বেঁধে দিন। এতে AI আপনার সহকারী হয়, আপনার বদলে ভাবার যন্ত্র নয়:
AI-এর নকশা হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভালো নকশা ছোট হয়, আর নিয়ম ভাঙতে দেয় না।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলোই আপনাকে কোড-লেখক থেকে সিস্টেম-চিন্তক বানাবে।
প্র ০১Alan Kay বলেছিলেন object হলো “ছোট ছোট কম্পিউটার, যারা একে অপরকে বার্তা পাঠায়।” এই ছবিটা Java-র method call-এর সাথে কীভাবে মেলে, আর কোথায় মেলে না?
Alan Kay ১৯৬০-এর দশকের শেষে “object-oriented” শব্দটি ব্যবহার শুরু করেন, আর ১৯৭০-এর দশকে Xerox PARC-এ তাঁর দল তৈরি করে Smalltalk. Kay-এর পড়াশোনা ছিল গণিত আর জীববিজ্ঞানে, আর তাঁর ছবিটাও জীববিজ্ঞান থেকে নেওয়া: শরীরের প্রতিটি কোষ নিজের ভেতরটা নিজে সামলায়, বাইরের সাথে যোগাযোগ করে শুধু রাসায়নিক সংকেতে। আরেকটি ছবি ছিল নেটওয়ার্ক — প্রতিটি object যেন একটি আলাদা কম্পিউটার, ভেতরে কী আছে কেউ জানে না, শুধু বার্তা আদান-প্রদান হয়। পরে এক চিঠিতে তিনি লেখেন, তাঁর কাছে OOP মানে মূলত বার্তা পাঠানো, নিজের অবস্থা লুকিয়ে রাখা আর যতটা সম্ভব দেরিতে ঠিক করা কোন কোড চলবে (late binding).
Java-তে wallet.pay(500) লিখলে এই ছবির অনেকটাই পাওয়া যায়। ডাকনেওয়ালা জানে না pay-এর ভেতরে কী হয়; Wallet নিজে সিদ্ধান্ত নেয়। আর late binding-ও আছে: wallet যদি আসলে একটি MerchantWallet হয়, তাহলে চালানোর সময় JVM ঠিক করে কোন সংস্করণের pay চলবে — একে বলে dynamic dispatch (পর্ব ২০)।
কোথায় মেলে না? Kay-এর object-এ সবকিছু object; Smalltalk-এ সংখ্যা ৩-ও object, আর 3 + 4 মানে ৩-কে “+ 4” বার্তা পাঠানো। Java-তে int একটি primitive, object নয় — গতির জন্য নেওয়া সিদ্ধান্ত। তা ছাড়া Java-র method call আসলে একই প্রোগ্রামের ভেতরে সরাসরি ডাকা, সত্যিকারের আলাদা “কম্পিউটার”-এর মধ্যে চিঠি পাঠানো নয়; ডাকনেওয়ালা উত্তর না আসা পর্যন্ত অপেক্ষা করে। Kay-এর ছবির সবচেয়ে কাছাকাছি আজকের microservice — নেটওয়ার্কে বার্তা পাঠানো স্বাধীন প্রোগ্রাম — আর Erlang-এর process বা actor model.
মূল উপলব্ধি: method call-কে “ফাংশন ডাকা” না ভেবে “অনুরোধ পাঠানো” ভাবলে নকশা ভালো হয় — কারণ তখন স্বাভাবিকভাবেই প্রশ্ন আসে, সিদ্ধান্তটা কার নেওয়া উচিত।
প্র ০২প্রথম object-oriented ভাষা Simula 67 জাহাজঘাটের simulation থেকে কেন জন্ম নিল? সেই ভাষার কোন ধারণাগুলো আজও Java-তে আছে?
১৯৬০-এর দশকের শুরুতে নরওয়ের Norwegian Computing Center-এ Kristen Nygaard কাজ করছিলেন operations research নিয়ে — জাহাজঘাটে কয়টি ক্রেন রাখলে জাহাজের অপেক্ষা কমবে, কারখানায় কাজের লাইন কীভাবে সাজালে উৎপাদন বাড়বে। এমন প্রশ্নের উত্তর গণিতের সূত্রে বের করা কঠিন; তাই কম্পিউটারে পুরো ব্যবস্থার একটি নকল (simulation) চালাতে হয়। প্রোগ্রামার Ole-Johan Dahl-কে নিয়ে তিনি প্রথমে Simula I তৈরি করেন, তারপর ১৯৬৭ সালে আরও সাধারণ ভাষা Simula 67.
Simulation-এ ধারণাটি প্রায় আপনা থেকেই আসে: প্রতিটি জাহাজ, প্রতিটি ক্রেন একেকটি আলাদা “জিনিস”, যার নিজের অবস্থা আছে (জাহাজ অপেক্ষায় আছে, মাল নামাচ্ছে) আর নিজের আচরণ আছে। একই ধরনের অনেক জাহাজের জন্য একটি নকশা — এটাই class, আর প্রতিটি আসল জাহাজ একটি object. মালবাহী জাহাজ আর যাত্রীবাহী জাহাজ দুটোই জাহাজ, কিন্তু কিছু আচরণ আলাদা — এখান থেকে এল subclass (আজকের inheritance) আর virtual procedure, যেখানে subclass নিজের সংস্করণ দিতে পারে।
আজকের Java-তে প্রায় সবই আছে: class, new দিয়ে object তৈরি, extends দিয়ে subclass, আর Java-তে সব সাধারণ method ডিফল্টভাবে “virtual” — subclass চাইলে বদলাতে পারে। Simula-তে স্বয়ংক্রিয় মেমরি ব্যবস্থাপনাও (garbage collection) ছিল — ধারণাটি অবশ্য প্রথম আসে Lisp-এ, ১৯৫৯–৬০ সালের দিকে John McCarthy-র হাতে; Simula সেটিকে object-এর জগতে নিয়ে আসে। আজ এটি Java-র মূল বৈশিষ্ট্য (পর্ব ০৫ ও ৪৪)। ধারণাগুলো সরাসরি নয়, ঘুরপথে এসেছে — Simula থেকে Smalltalk আর C++ (Bjarne Stroustrup পিএইচডির সময় Simula ব্যবহার করেছিলেন), সেখান থেকে Java.
দীর্ঘদিন এই অবদান তেমন স্বীকৃতি পায়নি। ২০০১ সালে Dahl ও Nygaard যৌথভাবে কম্পিউটার বিজ্ঞানের সর্বোচ্চ সম্মান Turing Award পান; পরের বছর, ২০০২ সালে, দুজনেই মারা যান।
মূল উপলব্ধি: OOP কোনো বিমূর্ত তত্ত্ব থেকে আসেনি, এসেছে একটি বাস্তব প্রয়োজন থেকে — অনেকগুলো স্বাধীন জিনিসের একসাথে চলা ব্যবস্থাকে প্রোগ্রামে ধরা। সমস্যাটা যখন সত্যিই এমন, তখনই object সবচেয়ে বেশি কাজে লাগে।
প্র ০৩দামি ডিজাইন সফটওয়্যার থাকতে Ward Cunningham আর Kent Beck কেন সাধারণ ইনডেক্স কার্ডের কথা বললেন? CRC কার্ড আজও কেন কাজে লাগে?
১৯৮০-এর দশকের শেষে Tektronix কোম্পানিতে Ward Cunningham আর Kent Beck Smalltalk-এ কাজ করতেন। তাঁরা লক্ষ করলেন, procedural প্রোগ্রামিংয়ে অভ্যস্ত মানুষেরা object-ভিত্তিক চিন্তা ধরতে খুব কষ্ট পান — ভাষার ব্যাকরণ শিখে ফেলেন, কিন্তু নকশা করেন আগের মতোই, একটি বড় “main” ঘিরে। ১৯৮৯ সালের OOPSLA সম্মেলনে তাঁদের প্রবন্ধ “A Laboratory For Teaching Object-Oriented Thinking” এই সমস্যার সমাধান দেয়: সাধারণ ইনডেক্স কার্ড, প্রতিটিতে Class, Responsibilities, Collaborators.
কার্ড কেন? প্রথমত, ছোট আকার একটি সীমা চাপিয়ে দেয়: একটি কার্ডে দায়িত্ব না ধরলে class বেশি বড়। দ্বিতীয়ত, কার্ড হাতে নেওয়া যায়। দলের মানুষেরা একেকটি কার্ড হাতে নিয়ে “আমি Wallet, আমি এখন Transaction-কে বলছি…” — এভাবে অভিনয় করলে মানুষ object-এর চোখ দিয়ে ভাবতে শুরু করে। তৃতীয়ত, কার্ড সস্তা — ছিঁড়ে ফেলা যায়। দামি টুলে নকশা আঁকলে মানুষ সেটাকে “শেষ” ভাবে আর বদলাতে চায় না; কাগজের কার্ডে কেউ মায়া রাখে না।
এই দুজনের প্রভাব কার্ডের চেয়ে অনেক বড়। Cunningham ১৯৯৫ সালে প্রথম wiki তৈরি করেন — Wikipedia-র পূর্বসূরি ধারণা। Beck পরে Extreme Programming আর test-driven development জনপ্রিয় করেন, আর Erich Gamma-র সাথে মিলে লেখেন Java-র বিখ্যাত টেস্টিং লাইব্রেরি JUnit (পর্ব ৪৭)।
আজ কেন কাজে লাগে? কারণ AI কোড লিখতে পারে, কিন্তু আপনার ব্যবসার নিয়ম আর দায়িত্ব-ভাগ নিয়ে দলের সবার একমত হওয়া — এটা এখনো মানুষের কাজ। একটি টেবিলে বসে ৩০ মিনিটের CRC সেশন অনেক সময় এমন ভুল ধরে, যা কোডে ধরতে সপ্তাহ লাগত।
মূল উপলব্ধি: নকশার হাতিয়ার যত সস্তা আর হালকা, মানুষ তত সহজে সেটা বদলায় — আর নকশার শুরুতে বারবার বদলানোই সবচেয়ে কম খরচের কাজ।
প্র ০৪Python বা C++ যেখানে class ছাড়াই ফাংশন লিখতে দেয়, সেখানে Java কেন সব কোড class-এর ভেতরে রাখতে বাধ্য করে? এর লাভ আর দাম কী?
১৯৯০-এর দশকের শুরুতে Sun Microsystems-এ James Gosling-এর দল যে ভাষা বানাচ্ছিলেন (Java 1.0 প্রকাশিত হয় ১৯৯৬ সালে), তার লক্ষ্য ছিল C++-এর চেয়ে সরল আর নিরাপদ একটি ভাষা, যা নেটওয়ার্কে নানা যন্ত্রে চলবে। তাঁরা C++-এর অনেক কিছু বাদ দেন — global variable, আলাদা free function, pointer-এর হিসাব। ফল: সবকিছু কোনো না কোনো class-এর সদস্য।
লাভ কী? প্রথমত, প্রতিটি নামের একটি “ঠিকানা” থাকে — Math.max, Integer.parseInt; কোনো নাম পুরো প্রোগ্রামে ভাসমান থাকে না, ফলে বড় প্রজেক্টে নামের সংঘাত কম। দ্বিতীয়ত, JVM-এর কাজের একক হলো class: প্রতিটি class compile হয়ে আলাদা .class ফাইলে যায়, আর JVM দরকার হলে class ধরে ধরে লোড করে (পর্ব ০৫)। নেটওয়ার্ক থেকে কোড আনার যুগে এটা খুব সুবিধাজনক ছিল। তৃতীয়ত, ভাষাটা সব জায়গায় একই রকম দেখায় — অন্যের কোড পড়া সহজ।
দাম কী? ছোট প্রোগ্রামেও আনুষ্ঠানিকতা: একটি “Hello” ছাপতে class, public static void main — নতুনদের জন্য ভয়ের। আর যে কোড আসলে শুধু ফাংশন, তাকেও কৃত্রিম class-এ ঢোকাতে হয় (Math, Collections-এর মতো “utility class”). তা ছাড়া Java সম্পূর্ণ object-oriented-ও নয়: int, double primitive, object নয়।
Java ধীরে ধীরে এই দাম কমিয়েছে। Java 8 (২০১৪) আনে lambda, যাতে একটি ছোট কাজকে মান হিসেবে পাঠানো যায় (পর্ব ৩৪)। Java 16 থেকে record স্থায়ী (পর্ব ২৫)। আর Java 21-এ preview হিসেবে আসে এমন সুবিধা, যাতে ছোট প্রোগ্রামে class না লিখেই void main() লেখা যায়; পরে Java 25-এ এটি স্থায়ী হয়েছে। তবে লক্ষ করুন, তখনো compiler আড়ালে একটি class বানিয়ে নেয় — মূল নিয়ম বদলায়নি, শুধু লুকানো হয়েছে।
মূল উপলব্ধি: Java-র “সব class-এ” নিয়ম বড় দলের শৃঙ্খলার জন্য নেওয়া একটি নকশা-সিদ্ধান্ত; এর মানে “সবকিছু object” নয়। class-কে কখনো কখনো শুধু একটি ঘর হিসেবে ব্যবহার করাও সম্পূর্ণ বৈধ।
প্র ০৫“বিশেষ্য থেকে class” কৌশলের সীমা কোথায়? Steve Yegge-এর “Execution in the Kingdom of Nouns” লেখাটি Java নিয়ে কী অভিযোগ করেছিল?
২০০৬ সালে প্রোগ্রামার Steve Yegge তাঁর ব্লগে একটি ব্যঙ্গাত্মক লেখা প্রকাশ করেন: “Execution in the Kingdom of Nouns.” গল্পটা এক কাল্পনিক রাজ্যের, যেখানে বিশেষ্যরা রাজা, আর ক্রিয়ারা (কাজ) নিজে থেকে কোথাও যেতে পারে না — সবসময় কোনো বিশেষ্যের সাথে থাকতে হয়। এটা ছিল Java-র রূপক: সেই সময়ের Java-তে একটি কাজ (যেমন “ময়লা ফেলা”) আলাদা করে পাঠানো যেত না; তাকে মুড়তে হতো কোনো object-এ। ফলে কোডে ভরে যেত GarbageDisposer, RequestHandler, …Manager, …Factory ধরনের নাম — যাদের আসলে একটাই কাজ, একটি ক্রিয়া।
লেখাটি ব্যঙ্গ, কিন্তু অভিযোগটা গুরুতর: সব কিছুকে বিশেষ্য বানালে নকশা ফাঁপা হয়ে যায়। “Manager” শব্দের class প্রায়ই ইঙ্গিত দেয় যে কেউ একটি ক্রিয়াকে জোর করে বিশেষ্য বানিয়েছেন।
বিশেষ্য কৌশলের আরও সীমা আছে। (১) সব বিশেষ্য class নয়: “মাস”, “দূরত্ব”, “অ্যাপ” — এগুলো ডেটা বা পুরো সিস্টেম। (২) গুরুত্বপূর্ণ class অনেক সময় ক্রিয়া থেকে আসে: “ধার নেওয়া” থেকে Loan, “পেমেন্ট করা” থেকে Payment, “রাইড চাওয়া” থেকে Trip. (৩) ভাষার ওপর নির্ভরতা: একই বিবরণ দুজন দুভাবে লিখলে বিশেষ্যের তালিকা বদলে যায়। (৪) কিছু সমস্যা স্বভাবতই রূপান্তর: “লেনদেনের তালিকা থেকে মাসিক রিপোর্ট বানাও” — এখানে object-এর চেয়ে একটি পাইপলাইন বেশি স্বাভাবিক।
Java নিজেও এই সমালোচনার জবাব দিয়েছে। Java 8 থেকে lambda আর method reference এসেছে, যাতে একটি কাজকে সরাসরি পাঠানো যায় — list.forEach(System.out::println) — কোনো কৃত্রিম class ছাড়া। Stream API (পর্ব ৩৫) রিপোর্ট বানানোর মতো রূপান্তরকে পাইপলাইনে লিখতে দেয়।
মূল উপলব্ধি: বিশেষ্য দিয়ে শুরু করুন, কিন্তু প্রতিটি সম্ভাব্য class-কে জিজ্ঞেস করুন “এর কি নিজের অবস্থা আর নিয়ম আছে?” না থাকলে সেটা ডেটা, record বা একটি সাধারণ method — রাজ্যটা বিশেষ্য আর ক্রিয়া দুজনেরই।
অনুশীলন
খাতা-কলম নিন। এগুলোর বেশিরভাগে কোড লিখতে হবে না — ভাবা আর নকশা করাই আসল কাজ।
-
লাইব্রেরির নকশা: বিবরণটি পড়ুন — “সদস্য কাউন্টারে গিয়ে বই ধার নেন। একজন সদস্য একসাথে সর্বোচ্চ তিনটি বই রাখতে পারেন। ১৪ দিনের মধ্যে ফেরত না দিলে প্রতিদিন ৫ টাকা জরিমানা। লাইব্রেরিয়ান নতুন বই তালিকায় যোগ করেন।” বিশেষ্য ও ক্রিয়া দাগিয়ে class, method আর অন্তত দুটি “কখনো না” নিয়ম বের করুন।
বিশেষ্য: সদস্য, কাউন্টার, বই, দিন, জরিমানা, লাইব্রেরিয়ান, তালিকা। ক্রিয়া: ধার নেওয়া, রাখা, ফেরত দেওয়া, জরিমানা হওয়া, যোগ করা।
সম্ভাব্য class:
Member(নাম, সদস্য নম্বর, এখন কয়টি বই আছে;canBorrow()),Book(শিরোনাম, লেখক, এখন ধার দেওয়া কি না),Loan(কোন বই, কোন সদস্য, ধারের তারিখ;fine(today)),Catalog(বইয়ের তালিকা;add(book),find(title)). “কাউন্টার” আর “দিন” আলাদা class নয়; “লাইব্রেরিয়ান” হয়তো শুধু অনুমতির একটি ভূমিকা।নিয়ম: (১) একজন সদস্যের হাতে কখনো তিনটির বেশি বই নয় — রক্ষা করবে
Member; (২) একটি বই একসাথে দুজনের কাছে ধার থাকতে পারে না — রক্ষা করবেBook; (৩) জরিমানা কখনো ঋণাত্মক নয় — রক্ষা করবেLoan. লক্ষ করুন, Loan একটি ক্রিয়া থেকে জন্ম নেওয়া class. -
স্কুলের CRC কার্ড: একটি স্কুলের ফলাফল ব্যবস্থার জন্য
Student,Course,Result— তিনটি CRC কার্ড লিখুন। তারপর ঠিক করুন: একজন ছাত্রের GPA কে হিসাব করবে, আর কেন?Student — R: নাম ও রোল জানে, নিজের সব Result-এর তালিকা রাখে, নিজের GPA জানায়। C: Result.
Course — R: বিষয়ের নাম, কোড, ক্রেডিট জানে; পাস নম্বর কত, জানে। C: নেই (শুধু তথ্য দেয়)।
Result — R: কোন ছাত্র, কোন বিষয়, প্রাপ্ত নম্বর জানে; নম্বর থেকে গ্রেড পয়েন্ট বের করে; নম্বর কখনো ০-এর কম বা পূর্ণমানের বেশি হতে দেয় না। C: Course (ক্রেডিট ও পাস নম্বরের জন্য)।
GPA কে হিসাব করবে?
Student— কারণ GPA একজন ছাত্রের সব Result-এর ওপর নির্ভর করে, আর সেই তালিকা Student-এর কাছেই আছে। প্রতিটি Result নিজের গ্রেড পয়েন্ট দেয় (তথ্যটা তার), Student সেগুলো ক্রেডিট দিয়ে গড় করে। কোনো আলাদাGpaManagerলাগে না — তাহলে Student anemic হয়ে যেত। -
হাতে dry run: অংশ ৮-এর
faremethod-এর জন্য তিনটি ডাক হাতে চালান:fare(2, true),fare(-3, true),fare(3, true). প্রতিটির জন্যtotal-এর মান ধাপে ধাপে লিখুন। কোনটিতে ভাগের সময় ভগ্নাংশ কেটে যায়?fare(2, true): km ≤ 0? না → total = ৪০ + ৫০ = ৯০ → রাত, তাই ৯০ / ৫ = ১৮ যোগ → ১০৮. ভাগ নিখুঁত, কিছু কাটেনি।
fare(-3, true): km ≤ 0? হ্যাঁ → সরাসরি ০ ফেরত। রাতের নিয়ম পর্যন্ত যাওয়াই হয়নি — edge case আগে ধরা হয়েছে বলে।
fare(3, true): total = ৪০ + ৭৫ = ১১৫ → ১১৫ / ৫ = ২৩ → ১৩৮. এখানেও নিখুঁত। আসলে ৪০ + ২৫ × km সবসময় ৫ দিয়ে বিভাজ্য (৪০ আর ২৫ দুটোই ৫-এর গুণিতক), তাই এই নিয়মে কখনো ভগ্নাংশ কাটে না! কিন্তু ভিত্তি ভাড়া যদি ৪২ হয়, তাহলে কাটবে — নিয়ম বদলালে dry run আবার করতে হয়।
-
Wallet-এ deposit যোগ করুন: অংশ ৭-এর কোডটি পাতাতেই বদলে চালান —
Wallet-এ একটিdeposit(long amount)method যোগ করুন, যা শূন্য বা ঋণাত্মক অঙ্ক প্রত্যাখ্যান করে (falseফেরত দেয়)। তারপরmain-এ ৩০০ জমা দিয়ে শেষ ব্যালান্স ছাপুন।pay-এর ঠিক নিচে এই method যোগ করুন:boolean deposit(long amount) { if (amount <= 0) return false; // reject a bad amount balance += amount; return true; }main-এ শেষ লাইনের আগেSystem.out.println("deposit 300: " + rina.deposit(300));যোগ করলে আউটপুটেdeposit 300: trueআর শেষেRina has 1000আসবে (৭০০ + ৩০০)। লক্ষ করুন, নিয়মটা Wallet-এর ভেতরে গেছে,main-এ নয় — দায়িত্ব ডেটার মালিকের কাছে। তবেrina.balance = -5000লেখার রাস্তা এখনো খোলা; সেটা বন্ধ হবে পর্ব ১৭-এ।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমসমস্যার বিবরণ থেকে যাচাই করা Java কোডে যাওয়ার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- 📄 সাধারণ ভাষায় সমস্যার বিবরণ পড়া
- 🖍️ বিশেষ্য দাগিয়ে সম্ভাব্য class খোঁজা
- 🎯 ক্রিয়া থেকে দায়িত্ব আর “কখনো না” নিয়ম ঠিক করা
- 🃏 CRC কার্ড হাতে দৃশ্য অভিনয় করে ফাঁক খোঁজা
- ☕ field আর method দিয়ে Java class লেখা
- 🔍 dry run আর edge case দিয়ে কোড যাচাই করা
Java-তে একটি object-এর state আর behaviour সাধারণত কীসের মাধ্যমে প্রকাশ পায়?
- package আর import দিয়ে
- State থাকে field-এ, behaviour লেখা হয় method হিসেবে
- State থাকে method-এ, behaviour থাকে field-এ
- দুটোই comment আকারে লেখা হয়
CRC কার্ডের তিনটি অংশ কী কী?
- Compile, Run, Check
- Class, Return type, Constructor
- Class, Responsibilities, Collaborators
- Code, Review, Commit
Python বা C++-এর তুলনায় Java-র কোন নিয়মটি সঠিক?
- প্রতিটি method কোনো না কোনো class-এর ভেতরে থাকতে হয়, main-ও
- Java-তে class-এর বাইরে global ফাংশন লেখা যায়
- Java-তে int, double-সহ সবকিছুই object
- ছোট প্রোগ্রামে class লাগে না, শুধু বড় প্রোগ্রামে লাগে
একটি রাইড অ্যাপে AppManager নামের একটি class রাইড বুকিং, পেমেন্ট, রেটিং, SMS আর রিপোর্ট — সব কাজ একাই করে। নকশার ভাষায় একে কী বলে?
- Anemic class
- Record
- Collaborator
- God class
“একটি Trip-এ একজন Driver থাকেন” — Trip আর Driver-এর সম্পর্ক কোনটি?
- is-a (inheritance)
- has-a (composition)
- কোনো সম্পর্ক নেই
- Driver হলো Trip-এর subclass
Object-ভিত্তিক নকশায় wallet.pay(500) লেখাটিকে সবচেয়ে ভালোভাবে বর্ণনা করে কোনটি?
- বাইরের কোড ওয়ালেটের ব্যালান্স থেকে নিজে ৫০০ বিয়োগ করছে
- একটি নতুন wallet object তৈরি হচ্ছে
- wallet object-কে “৫০০ পে করো” বার্তা পাঠানো হচ্ছে, সিদ্ধান্ত নেবে wallet নিজেই
- শুধু ওয়ালেটের ব্যালান্স পড়া হচ্ছে, কিছু বদলাবে না
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Object = state + behaviour; Java-তে state থাকে field-এ, behaviour লেখা হয় method হিসেবে। Class নকশা, object আসল জিনিস।
- বিবরণের বিশেষ্য → সম্ভাব্য class, ক্রিয়া → method, “কখনো না” লাইন → নিয়ম। কিছু class ক্রিয়া থেকেও জন্মায় (Trip, Loan).
- CRC কার্ড দিয়ে দায়িত্ব ভাগ করুন আর দৃশ্য অভিনয় করে ফাঁক খুঁজুন; প্রতিটি class-এর দায়িত্ব এক বাক্যে বলা যাওয়া চাই।
- Object-রা ডেটায় হাত দেয় না, বার্তা পাঠায়; যার ডেটা, নিয়ম রক্ষার দায়ও তার। সম্পর্ক বেশিরভাগ সময় has-a, কম ক্ষেত্রে is-a.
- Java-তে সব কোড class-এ, কিন্তু সব কিছু object নয় — অবস্থাহীন হিসাবের জন্য static method যথেষ্ট। AI-এর নকশায় অতিরিক্ত class, god class আর anemic class খুঁজুন।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ০৩ · সেটআপ — JDK, IDE আর JShellপরবর্তী পর্বনিজের কম্পিউটারে Java চালানোর সব প্রস্তুতি।
- Java প্রোগ্রামিং · কোর্স হোম৫৯টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- C++ কোর্স · বস্তু দিয়ে ভাবাপাশের ট্র্যাকএকই চিন্তা C++-এর চোখে — procedural বনাম object আর invariant.
- Python কোর্স · সমস্যা সমাধানভিত্তিComputational thinking, pseudocode, dry run আর edge case — বিস্তারিত।