Advance JavaScript শিখুন বাংলায় (পর্ব ৮) — OOP: Inheritance (extends, super) শিখুন

OOP - Inheritance

আগের পর্বে আমরা Class আর Constructor শিখেছিলাম। এই পর্বে আমরা দেখব Inheritance (উত্তরাধিকার) — কীভাবে একটা ক্লাস আরেকটা ক্লাসের প্রপার্টি আর মেথড "উত্তরাধিকারসূত্রে" পেতে পারে, যাতে একই কোড বারবার লিখতে না হয়।

সমস্যাটা কী?

ধরো Pathok Ghor-এ দুই ধরনের কন্টেন্ট আছে — বই আর অডিওবুক। দুটোরই title, author আছে, কিন্তু অডিওবুকের অতিরিক্ত duration আছে। আলাদা আলাদা ক্লাস লিখলে কমন অংশটা বারবার লিখতে হয়:

// ❌ দুটো ক্লাসে একই title, author কোড বারবার লেখা হচ্ছে
class Book {
  constructor(title, author) {
    this.title = title;
    this.author = author;
  }
  getInfo() {
    return `${this.title} — ${this.author}`;
  }
}

class Audiobook {
  constructor(title, author, duration) {
    this.title = title; // Book-এর সাথে ডুপ্লিকেট
    this.author = author; // Book-এর সাথে ডুপ্লিকেট
    this.duration = duration;
  }
  getInfo() {
    return `${this.title} — ${this.author}`; // এখানেও ডুপ্লিকেট
  }
}

extends দিয়ে সমাধান

extends কীওয়ার্ড দিয়ে একটা ক্লাস আরেকটা ক্লাসের সব প্রপার্টি ও মেথড "উত্তরাধিকারসূত্রে" পেয়ে যায়, শুধু নতুন যা লাগবে সেটুকু যোগ করলেই হয়।

class Book {
  constructor(title, author) {
    this.title = title;
    this.author = author;
  }

  getInfo() {
    return `${this.title} — ${this.author}`;
  }
}

// ✅ Audiobook, Book-এর সবকিছু পাচ্ছে, শুধু duration নতুন
class Audiobook extends Book {
  constructor(title, author, duration) {
    super(title, author); // Book-এর constructor কল করা
    this.duration = duration;
  }
}

const book = new Book("সোনার তরী", "রবীন্দ্রনাথ ঠাকুর");
const audiobook = new Audiobook("পথের পাঁচালী", "বিভূতিভূষণ", "৪ ঘণ্টা");

console.log(book.getInfo()); // সোনার তরী — রবীন্দ্রনাথ ঠাকুর
console.log(audiobook.getInfo()); // পথের পাঁচালী — বিভূতিভূষণ (Book থেকে পাওয়া মেথড!)
console.log(audiobook.duration); // ৪ ঘণ্টা

Audiobook ক্লাসে getInfo() লেখাই হয়নি, তবুও কাজ করছে — কারণ এটা Book থেকে উত্তরাধিকারসূত্রে পেয়েছে।

super কীওয়ার্ড

super দুই জায়গায় ব্যবহার হয়:

ব্যবহার কাজ
super(...) প্যারেন্ট ক্লাসের constructor কল করে (constructor-এর প্রথম লাইনে থাকতেই হয়)
super.methodName() প্যারেন্ট ক্লাসের কোনো মেথড সরাসরি কল করে

গুরুত্বপূর্ণ নিয়ম: চাইল্ড ক্লাসে constructor লিখলে, this ব্যবহার করার আগে অবশ্যই super() কল করতে হবে — নাহলে JavaScript এরর দেবে।

class Audiobook extends Book {
  constructor(title, author, duration) {
    this.duration = duration; // ❌ ReferenceError — super() আগে কল করতে হবে
    super(title, author);
  }
}

Method Override — নিজের মতো পাল্টানো

চাইল্ড ক্লাস চাইলে প্যারেন্টের কোনো মেথড নিজের মতো করে "override" (পুনর্লিখন) করতে পারে, প্রয়োজনে super.methodName() দিয়ে প্যারেন্টের ভার্সনও ব্যবহার করে নিতে পারে।

class Audiobook extends Book {
  constructor(title, author, duration) {
    super(title, author);
    this.duration = duration;
  }

  // Book-এর getInfo() override করা হচ্ছে
  getInfo() {
    const baseInfo = super.getInfo(); // প্যারেন্টের ভার্সন কল করে ফলাফল ব্যবহার
    return `${baseInfo} (${this.duration})`;
  }
}

const audiobook = new Audiobook("পথের পাঁচালী", "বিভূতিভূষণ", "৪ ঘণ্টা");
console.log(audiobook.getInfo()); // পথের পাঁচালী — বিভূতিভূষণ (৪ ঘণ্টা)

এখানে super.getInfo() ব্যবহার করে পুরো লজিক আবার না লিখে শুধু প্যারেন্টের ফলাফলের সাথে নতুন তথ্য জুড়ে দেওয়া হচ্ছে — কোড পুনর্ব্যবহারের একটা সুন্দর উদাহরণ।

instanceof দিয়ে চেক করা

instanceof দিয়ে যাচাই করা যায় কোনো অবজেক্ট নির্দিষ্ট ক্লাস (বা তার প্যারেন্ট) থেকে বানানো কিনা:

console.log(audiobook instanceof Audiobook); // true
console.log(audiobook instanceof Book); // true — কারণ Audiobook, Book-কে extend করেছে
console.log(book instanceof Audiobook); // false — Book, Audiobook-এর instance না

লাইভ ডেমো — বেস ক্লাস বনাম চাইল্ড ক্লাস

নিচের দুটো কার্ড দেখো — একটা সাধারণ বই, একটা অডিওবুক (যেটা Book-কে extend করেছে)। দুটোরই getInfo() আছে, কিন্তু অডিওবুকেরটা একটু ভিন্ন ফলাফল দেয়।

কখন Inheritance ব্যবহার করবে (আর কখন না)

Inheritance শক্তিশালী, কিন্তু সবসময় সেরা সমাধান না। একটা সাধারণ নিয়ম — "is-a" সম্পর্ক থাকলে Inheritance ঠিক আছে (Audiobook is a Book), কিন্তু "has-a" সম্পর্কে (একটা জিনিসের ভেতরে আরেকটা জিনিস থাকা) সাধারণত composition (অবজেক্টের ভেতরে অবজেক্ট) বেশি ভালো।

// ✅ ভালো — is-a সম্পর্ক (Audiobook is a Book)
class Audiobook extends Book { /* ... */ }

// ❌ খারাপ ডিজাইন — has-a সম্পর্কের জন্য inheritance ব্যবহার করা
class Library extends Book { /* Library কোনোভাবেই "এক ধরনের" Book না! */ }

// ✅ ভালো — composition ব্যবহার করা (Library-এর ভেতরে Book-এর array থাকে)
class Library {
  constructor() {
    this.books = []; // has-a: Library-এর কাছে বই আছে
  }
}

একটা সম্পূর্ণ উদাহরণ — কন্টেন্ট টাইপের হায়ারার্কি

চলো Pathok Ghor-এর জন্য একটা বাস্তব হায়ারার্কি বানাই — বেস Content ক্লাস, তার থেকে Book আর Article এক্সটেন্ড করা।

class Content {
  constructor(title, publishDate) {
    this.title = title;
    this.publishDate = publishDate;
    this.views = 0;
  }

  incrementViews() {
    this.views++;
  }

  getSummary() {
    return `"${this.title}" (${this.publishDate})`;
  }
}

class Book extends Content {
  constructor(title, publishDate, author, pageCount) {
    super(title, publishDate);
    this.author = author;
    this.pageCount = pageCount;
  }

  getSummary() {
    return `${super.getSummary()} — লেখক: ${this.author}, ${this.pageCount} পাতা`;
  }
}

class Article extends Content {
  constructor(title, publishDate, writer, readTimeMinutes) {
    super(title, publishDate);
    this.writer = writer;
    this.readTimeMinutes = readTimeMinutes;
  }

  getSummary() {
    return `${super.getSummary()} — লেখক: ${this.writer}, পড়তে লাগবে ${this.readTimeMinutes} মিনিট`;
  }
}

const book = new Book("সোনার তরী", "১৮৯৪", "রবীন্দ্রনাথ ঠাকুর", 120);
const article = new Article("JavaScript শেখা", "২০২৬", "আরিফ", 8);

console.log(book.getSummary());
// "সোনার তরী" (১৮৯৪) — লেখক: রবীন্দ্রনাথ ঠাকুর, 120 পাতা
console.log(article.getSummary());
// "JavaScript শেখা" (২০২৬) — লেখক: আরিফ, পড়তে লাগবে 8 মিনিট

book.incrementViews(); // Content থেকে পাওয়া মেথড, দুটোই ব্যবহার করতে পারে
article.incrementViews();

এখানে incrementViews() শুধু Content-এ একবার লেখা হয়েছে, কিন্তু Book আর Article দুটোই এটা ব্যবহার করতে পারছে — আর প্রতিটা তার নিজের getSummary()-এ super.getSummary() দিয়ে কমন অংশটুকু পুনর্ব্যবহার করছে।

নিজে চেষ্টা করো

Exercise:

  1. উপরের Content হায়ারার্কিতে একটা তৃতীয় চাইল্ড ক্লাস Video যোগ করো (durationMinutes প্রপার্টি সহ)।
  2. একটা Animal ক্লাস বানাও (name, makeSound() মেথড), তারপর Dog আর Cat ক্লাস দিয়ে extend করো, প্রতিটাতে makeSound() override করো।
  3. instanceof ব্যবহার করে চেক করো Dog-এর একটা instance Animal-এরও instance কিনা।
  4. ভেবে দেখো তোমার কোনো প্রজেক্টে (Mobile Khujo-তে ফোনের ব্র্যান্ড অনুযায়ী, বা Pathok Ghor-এ কন্টেন্ট টাইপ অনুযায়ী) Inheritance প্রয়োগ করা যায় কিনা।

পরের পর্বে যা থাকছে

পরের পর্বে আমরা ফিরে যাব async জগতে — localStorage দিয়ে ডেটা persist করা, এবার Class-ভিত্তিক ডেটা মডেলের সাথে মিলিয়ে কীভাবে জটিল অবজেক্ট সংরক্ষণ ও পুনরুদ্ধার করা যায় তা দেখব।

একটি মন্তব্য পোস্ট করুন

নবীনতর পূর্বতন

যোগাযোগ ফর্ম