পাঠ ০৭ · ৫৮-এর মধ্যে · মডিউল ২
Home / Courses / Concepts of Programming Languages & Compiler Design / OOP প্যারাডাইম

অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিং প্যারাডাইম

Object-oriented programming paradigm
৮ মিনিট পড়া শুরু · Beginner Python কোডসহ সম্পূর্ণ বাংলায়

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

  • OOP প্যারাডাইমের সংজ্ঞা এবং এটি ইম্পারেটিভ প্রোগ্রামিং-এর সাথে ঠিক কীভাবে সম্পর্কিত (L06-এর সরাসরি সম্প্রসারণ)
  • OOP-এর চার স্তম্ভ — এনক্যাপসুলেশন, ইনহেরিটেন্স, পলিমরফিজম, অ্যাবস্ট্রাকশন — নির্দিষ্ট সংজ্ঞাসহ
  • Python-এ একটি ছোট ক্লাস হায়ারার্কি দিয়ে পলিমরফিজম বাস্তবে কীভাবে কাজ করে তার প্রমাণ
  • এই কোর্সের পরবর্তী মডিউলে (M7 টাইপ সিস্টেম, M10 ডেটা অ্যাবস্ট্রাকশন) এই ধারণাগুলো কীভাবে আবার ফিরে আসবে

১ · OOP কী — ইম্পারেটিভ কোড গঠন করার একটি উপায়

অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিংObject-Oriented Programming (OOP)প্রোগ্রামকে অবজেক্টের চারপাশে সাজানো একটি প্যারাডাইম — প্রতিটি অবজেক্ট তার স্টেট ও বিহেভিয়ার একসাথে বহন করে। প্রোগ্রামকে অবজেক্টের চারপাশে সাজায় — প্রতিটি অবজেক্ট তার নিজের স্টেট (ডেটা/ফিল্ড) এবং সেই স্টেটের ওপর কাজ করা বিহেভিয়ার (মেথড) একসাথে বহন করে। L06-এ আমরা দেখেছি ইম্পারেটিভ প্রোগ্রামিং মিউটেবল ভ্যারিয়েবল ও এক্সপ্লিসিট স্টেট-চেঞ্জ নিয়ে কাজ করে — OOP সেই একই ইম্পারেটিভ-স্টাইল স্টেট-মিউটেশনকে প্রতিস্থাপন করে না, বরং সেটিকে একটি নির্দিষ্ট, গঠনবদ্ধ উপায়ে সংগঠিত করে: প্রতিটি স্টেট এখন কোনো না কোনো অবজেক্টের ভেতরে বাস করে, এবং সেই স্টেট পরিবর্তনের একমাত্র বৈধ পথ হলো সেই অবজেক্টেরই মেথড কল করা।

Shape (abstract) — area() ঘোষিত, বাস্তবায়ন নেই Rectangle.area() = width × height = 3×4 = 12 Circle.area() = π×r² = π×4 ≈ 12.566 shape.area() → রানটাইমে সঠিক ভার্সন কল হয় (পলিমরফিজম)
একই বেস ক্লাস Shape থেকে দুটি সাবক্লাস — কিন্তু .area() কল করলে প্রতিটি অবজেক্টের নিজস্ব বাস্তবায়ন স্বয়ংক্রিয়ভাবে কল হয়, কলিং কোডকে জানতেই হয় না কোনটা Rectangle আর কোনটা Circle।

২ · চারটি স্তম্ভ

এনক্যাপসুলেশনEncapsulationডেটা ও তার ওপর কাজ করা মেথড একসাথে বাঁধা, এবং বাইরে থেকে সরাসরি অভ্যন্তরীণ স্টেটে অ্যাক্সেস সীমিত/নিয়ন্ত্রিত রাখা।
ডেটা ও মেথড একসাথে বেঁধে রাখা, এবং বাইরে থেকে সরাসরি ইন্টারনাল স্টেট অ্যাক্সেস সীমিত রাখা — M10/L46-এর অ্যাবস্ট্রাক্ট ডেটা টাইপের একটি বাস্তবায়ন।
ইনহেরিটেন্সInheritanceএকটি নতুন ক্লাস বিদ্যমান একটি ক্লাসের ভিত্তিতে সংজ্ঞায়িত করা, তার গঠন ও বিহেভিয়ার পুনর্ব্যবহার/সম্প্রসারণ করা।
একটি নতুন ক্লাস বিদ্যমান ক্লাসের ভিত্তিতে সংজ্ঞায়িত করা — গঠন ও বিহেভিয়ার পুনর্ব্যবহার/সম্প্রসারণ করা। নিচের কোডে Rectangle ও Circle দুটোই Shape-এর সন্তান।
পলিমরফিজমPolymorphismএকই মেথড কল, অবজেক্টের রানটাইম টাইপ অনুযায়ী ভিন্ন আচরণ প্রদর্শন করে।
একই মেথড কল — shape.area() — অবজেক্টের রানটাইম টাইপ অনুযায়ী ভিন্ন আচরণ দেয়। M7/L35-এ টাইপ-সিস্টেমের দৃষ্টিকোণ থেকে আরও গভীরে যাব।
অ্যাবস্ট্রাকশনAbstractionশুধু প্রাসঙ্গিক/প্রয়োজনীয় ইন্টারফেস প্রকাশ করা, বাস্তবায়নের খুঁটিনাটি লুকিয়ে রাখা।
শুধু প্রয়োজনীয় ইন্টারফেস প্রকাশ করা, বাস্তবায়নের খুঁটিনাটি লুকানো — কলিং কোড শুধু জানে .area() আছে, কীভাবে হিসাব হয় তা জানার দরকার নেই।

৩ · পলিমরফিজম বাস্তবে — একটি Shape হায়ারার্কি

নিচের কোডে একটি বেস ক্লাস Shape এবং দুটি সাবক্লাস Rectangle ও Circle — প্রতিটি নিজস্ব সঠিক সূত্র দিয়ে area() ওভাররাইড করে। দুটো অবজেক্টই একটিমাত্র তালিকায় (shapes) রাখা হচ্ছে এবং একটিমাত্র অভিন্ন লুপ থেকে .area() কল হচ্ছে — লুপটি জানেও না কোনটা কোন সাবক্লাসের অবজেক্ট, তবু প্রতিটির জন্য সঠিক সূত্র স্বয়ংক্রিয়ভাবে কল হয়।

Python
import math

class Shape:
    """বেস ক্লাস — area() ঘোষিত কিন্তু বাস্তবায়ন নেই, প্রতিটি সাবক্লাসকে নিজেরটা দিতে হবে"""
    def area(self):
        raise NotImplementedError("সাবক্লাসকে অবশ্যই area() ওভাররাইড করতে হবে")

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return math.pi * self.radius ** 2

# দুটো ভিন্ন সাবক্লাসের অবজেক্ট একইসাথে একটি তালিকায়
shapes = [Rectangle(3, 4), Circle(2)]

# একটিমাত্র অভিন্ন লুপ -- লুপটি জানেই না কোনটা Rectangle, কোনটা Circle
for shape in shapes:
    print(f"{type(shape).__name__}: area = {shape.area():.3f}")

    
লক্ষ্য করুন — Rectangle(3, 4).area() দেয় 12 (3×4), আর Circle(2).area() দেয় math.pi * 4 ≈ 12.566 (π×r²)। দুটোই সঠিক, হাতে-যাচাই-করা সূত্র। মূল বিষয়টি হলো: লুপের কোডে কোথাও if isinstance(shape, Rectangle)-এর মতো কোনো টাইপ-চেক নেই — Python নিজে থেকেই রানটাইমে প্রতিটি অবজেক্টের সঠিক area() মেথড বেছে নেয়। এটিই পলিমরফিজমের মূল সুবিধা: নতুন একটি Triangle ক্লাস যোগ করলেও, শুধু area() বাস্তবায়ন করলেই লুপের কোড এক অক্ষরও না বদলে কাজ করবে।
DSA কোর্সের সাথে সম্পর্ক

একটি ক্লাস হায়ারার্কি (Shape → Rectangle/Circle) নিজেই একটি ট্রি গঠন করে — Data Structures & Algorithms কোর্সের ট্রি ধারণার একটি বাস্তব প্রয়োগ। এই একই "ট্রি-আকৃতির" চিন্তা পরে M5-এর পার্স ট্রিতেও ফিরে আসবে, যেখানে গ্রামারের নন-টার্মিনাল ও তাদের সাবক্লাস-সদৃশ এক্সপ্যানশনগুলো একই রকম হায়ারার্কিক্যাল কাঠামো তৈরি করে।

মূল কথা · Key takeaway

OOP ইম্পারেটিভ প্রোগ্রামিংকে বাতিল করে না — এটি স্টেট ও বিহেভিয়ারকে অবজেক্টের ভেতরে বেঁধে সংগঠিত করার একটি উপায়। চারটি স্তম্ভের মধ্যে পলিমরফিজম বিশেষভাবে গুরুত্বপূর্ণ কারণ এটি কোডকে এক্সটেনসিবল করে — নতুন সাবক্লাস যোগ করা যায় বিদ্যমান কোড না ভেঙে।

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

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

প্র ০১ উপরের কোডে shapes তালিকার লুপে যদি একটি নতুন Triangle ক্লাস যোগ করা হয় (যেটিও Shape-এর সাবক্লাস এবং নিজস্ব area() বাস্তবায়ন করে), লুপের কোড কি পরিবর্তন করতে হবে?

না — এটিই পলিমরফিজমের সবচেয়ে বড় সুবিধা। লুপটি লেখা আছে for shape in shapes: shape.area() — এটি কখনোই নির্দিষ্ট কোনো ক্লাসের নাম উল্লেখ করে না, শুধু ধরে নেয় প্রতিটি অবজেক্টের একটি area() মেথড আছে। যেকোনো নতুন Shape-সাবক্লাস, যতক্ষণ সে area() সঠিকভাবে বাস্তবায়ন করে, এই একই লুপে বিনা পরিবর্তনে কাজ করবে — এই বৈশিষ্ট্যকে প্রায়ই "ওপেন ফর এক্সটেনশন, ক্লোজড ফর মডিফিকেশন" নীতি বলা হয়।

প্র ০২ এনক্যাপসুলেশন ও অ্যাবস্ট্রাকশন — এই দুটো স্তম্ভ শোনায় প্রায় একই রকম। এদের মধ্যে পার্থক্য কী?

ঘনিষ্ঠভাবে সম্পর্কিত হলেও আলাদা। এনক্যাপসুলেশন হলো বাস্তবায়নের কৌশল — ডেটা ও মেথড একসাথে বেঁধে রাখা এবং সরাসরি বাইরের অ্যাক্সেস সীমিত করা (যেমন Python-এ নামের আগে আন্ডারস্কোর দিয়ে "প্রাইভেট" ইঙ্গিত দেওয়া)। অ্যাবস্ট্রাকশন হলো ডিজাইনের লক্ষ্য — ব্যবহারকারীকে শুধু প্রাসঙ্গিক ইন্টারফেস দেখানো, খুঁটিনাটি লুকানো। এনক্যাপসুলেশন হলো সেই লক্ষ্য অর্জনের একটি হাতিয়ার — আপনি এনক্যাপসুলেশন ব্যবহার করেই অ্যাবস্ট্রাকশন বাস্তবায়ন করেন।

প্র ০৩ Circle(2).area() চালালে ফলাফল ঠিক 12.566370614359172 (পূর্ণ নির্ভুলতায়) — কিন্তু কোড {shape.area():.3f} দিয়ে প্রিন্ট করছে। ফরম্যাট স্ট্রিং-এর :.3f অংশটি কী করছে?

:.3f হলো একটি ফরম্যাট স্পেসিফায়ার যা সংখ্যাটিকে দশমিকের পর ঠিক ৩ ঘর পর্যন্ত রাউন্ড করে দেখায় (fixed-point notation) — এটি গণনার প্রকৃত নির্ভুলতা পরিবর্তন করে না, শুধু প্রদর্শনের সময় সংক্ষিপ্ত করে। তাই math.pi * 4-এর প্রকৃত মান 12.566370614359172 থেকেই গণনা হয়, কিন্তু স্ক্রিনে দেখা যায় 12.566 — পড়ার সুবিধার জন্য, নির্ভুলতা কমানোর জন্য নয়।

অনুশীলন

  1. পরীক্ষা করুন: উপরের কোডে shapes তালিকায় আরেকটি Rectangle(5, 5) যোগ করে Run চাপুন — নতুন কোন লাইন প্রিন্ট হবে?

    নতুন একটি লাইন যোগ হবে: Rectangle: area = 25.000 (যেহেতু 5×5 = 25)। মোট প্রিন্ট হবে তিনটি লাইন — Rectangle 12.000, Circle 12.566, এবং নতুন Rectangle 25.000। লক্ষ্য করুন লুপের কোডে কোনো পরিবর্তন লাগেনি, শুধু ইনপুট তালিকায় একটি নতুন অবজেক্ট যোগ করতে হয়েছে — প্রশ্ন ০১-এর উত্তরের সরাসরি প্রমাণ।

  2. চিন্তা করুন: যদি Shape.area()-এ raise NotImplementedError(...)-এর বদলে শুধু pass (কিছু না করে None রিটার্ন) লেখা থাকত, তাহলে কেউ ভুলবশত area() ওভাররাইড করতে ভুলে গেলে কী সমস্যা হতো?

    বাগটি নীরবে (silently) ছড়িয়ে যেত — ভুলে-যাওয়া সাবক্লাসের .area() কল করলে কোনো এরর ছাড়াই None রিটার্ন হতো, এবং সেই None পরবর্তী কোনো হিসাবে (যেমন যোগফল) ব্যবহার হলে অনেক পরে, ভিন্ন জায়গায় একটি রহস্যময় TypeError দেখা দিত — ডিবাগ করা কঠিন হয়ে যেত। raise NotImplementedError ব্যবহার করে ভুলটি ঠিক যেখানে ঘটেছে (ওভাররাইড না করা সাবক্লাসে) সেখানেই, সাথে সাথেই, স্পষ্ট এরর মেসেজসহ ধরা পড়ে — একটি ভালো ডিজাইন প্র্যাকটিস।

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

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ M2 প্রোগ্রামিং প্যারাডাইম মডিউলের বাকি পাঠগুলো দেখুন — ফাংশনাল, লজিক ও মাল্টি-প্যারাডাইম।
  • আগের পাঠ L06 ইম্পারেটিভ প্রোগ্রামিং প্যারাডাইম — OOP যে স্টেট-মিউটেশন-নির্ভর ভিত্তির ওপর দাঁড়িয়ে, সেটাই এখানে ব্যাখ্যা করা হয়েছে।
  • পরের পাঠ L08 ফাংশনাল প্রোগ্রামিং প্যারাডাইম — OOP-এর ঠিক বিপরীত দর্শন: মিউটেবল স্টেটের বদলে বিশুদ্ধ ফাংশন ও ইমিউটেবিলিটি।
আগের পাঠ
ইম্পারেটিভ প্রোগ্রামিং প্যারাডাইম