Advance JavaScript শিখুন বাংলায় (পর্ব ৭) — OOP: Class ও Constructor শিখুন

OOP - Class ও Constructor

আগের পর্বে আমরা Promise বিস্তারিতভাবে শিখেছিলাম। এই পর্ব থেকে আমরা Object-Oriented Programming (OOP)-এ ঢুকছি — শুরুতেই Class আর Constructor, যা দিয়ে একই ধরনের একাধিক অবজেক্ট সুশৃঙ্খলভাবে তৈরি করা যায়।

সমস্যাটা কী?

ধরো একটা বইয়ের সিস্টেমে একাধিক বই অবজেক্ট বানাতে হবে — প্রতিটাতে একই ধরনের প্রপার্টি আর মেথড থাকবে। প্লেইন অবজেক্ট দিয়ে বারবার একই কাঠামো লেখা বিরক্তিকর আর ভুলপ্রবণ:

// ❌ প্রতিটা বই আলাদা করে এভাবে লিখতে হচ্ছে — বারবার একই স্ট্রাকচার
const book1 = {
  title: "সোনার তরী",
  author: "রবীন্দ্রনাথ ঠাকুর",
  views: 0,
  incrementViews() {
    this.views++;
  }
};

const book2 = {
  title: "পথের পাঁচালী",
  author: "বিভূতিভূষণ",
  views: 0,
  incrementViews() {
    this.views++;
  }
};

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

Class একটা "নকশা" (blueprint) — এখান থেকে একই কাঠামোর একাধিক অবজেক্ট (instance) সহজে তৈরি করা যায়।

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

  incrementViews() {
    this.views++;
  }

  getInfo() {
    return `${this.title} — ${this.author} (${this.views} ভিউ)`;
  }
}

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

book1.incrementViews();
book1.incrementViews();
console.log(book1.getInfo()); // সোনার তরী — রবীন্দ্রনাথ ঠাকুর (2 ভিউ)
console.log(book2.getInfo()); // পথের পাঁচালী — বিভূতিভূষণ (0 ভিউ)

Constructor কী করে?

constructor একটা বিশেষ মেথড যেটা new কীওয়ার্ড দিয়ে নতুন অবজেক্ট বানানোর সময় স্বয়ংক্রিয়ভাবে কল হয়। এখানে যা যা করা হয়:

ধাপ কী ঘটে
new Book(...) কল হলে একটা নতুন খালি অবজেক্ট তৈরি হয়
সেই অবজেক্টই constructor-এর ভেতরে this হয়ে যায়
this.title = title-এর মতো লাইনগুলো প্রপার্টি বসিয়ে দেয়
সম্পূর্ণ তৈরি হওয়া অবজেক্টটা স্বয়ংক্রিয়ভাবে রিটার্ন হয়

Method-গুলো কোথায় থাকে?

একটা গুরুত্বপূর্ণ বিষয় — ক্লাসের মেথড (incrementViews, getInfo) প্রতিটা instance-এ আলাদা করে কপি হয় না — এগুলো Book.prototype-এ একবারই থাকে, সব instance সেটা শেয়ার করে।

console.log(book1.incrementViews === book2.incrementViews); // true
// দুটো ভিন্ন বই অবজেক্ট, কিন্তু মেথড একটাই — মেমরি বাঁচে

এই কারণেই Class instance-ভিত্তিক ডেটা (title, views) মেমরি-এফিশিয়েন্টভাবে সামলাতে পারে, প্লেইন অবজেক্ট লিটারেল বারবার বানানোর চেয়ে ভালো।

Getter ও Setter

ক্লাসে get/set দিয়ে প্রপার্টির মতো দেখতে কিন্তু আসলে ফাংশন-চালিত অ্যাক্সেসর বানানো যায় — যেমন একটা "কম্পিউটেড" প্রপার্টি।

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

  // Getter — মেথড কল না করে যেন প্রপার্টির মতোই পড়া যায়
  get readingTime() {
    return Math.ceil(this._pageCount / 20); // ধরি মিনিটে ২০ পাতা পড়া যায়
  }

  // Setter — মান বসানোর সময় যাচাই করা যায়
  set pageCount(value) {
    if (value < 0) {
      throw new Error("পাতা সংখ্যা ঋণাত্মক হতে পারে না");
    }
    this._pageCount = value;
  }
}

const book = new Book("সোনার তরী", "রবীন্দ্রনাথ ঠাকুর", 120);
console.log(book.readingTime); // 6 — মেথডের মতো () ছাড়াই কল হচ্ছে

book.pageCount = 200; // setter কল হচ্ছে
console.log(book.readingTime); // 10

Static মেথড

static মেথড কোনো instance-এ না, ক্লাসের উপরেই সরাসরি কল হয় — এটা এমন হেল্পার ফাংশনের জন্য উপযুক্ত যা কোনো নির্দিষ্ট instance-এর ডেটা লাগে না।

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

  // static মেথড — একটা রেডিমেড ফরম্যাট থেকে Book বানায়
  static fromString(str) {
    const [title, author] = str.split(" - ");
    return new Book(title, author);
  }
}

// instance বানানো ছাড়াই সরাসরি ক্লাসের উপর কল
const book = Book.fromString("সোনার তরী - রবীন্দ্রনাথ ঠাকুর");
console.log(book.title); // সোনার তরী

Private ফিল্ড (#)

আধুনিক JavaScript-এ # দিয়ে সত্যিকারের প্রাইভেট প্রপার্টি বানানো যায় — এগুলো ক্লাসের বাইরে থেকে একদমই অ্যাক্সেস করা যায় না (আগে যেটা Closure দিয়ে সিমুলেট করতাম, পর্ব ১-এ)।

class BankAccount {
  #balance; // প্রাইভেট ফিল্ড ঘোষণা

  constructor(initialBalance) {
    this.#balance = initialBalance;
  }

  deposit(amount) {
    this.#balance += amount;
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
console.log(account.#balance); // ❌ SyntaxError — বাইরে থেকে একদমই অ্যাক্সেস করা যায় না

লাইভ ডেমো — বুক ক্লাস দিয়ে ভিউ কাউন্টার

নিচে দুটো "বই" instance আছে, প্রতিটা একটা Class থেকে বানানো — একটার ভিউ বাড়ালে আরেকটার উপর কোনো প্রভাব পড়ে না।

সোনার তরী

ভিউ: 0

পথের পাঁচালী

ভিউ: 0

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

চলো Constructor, getter, static মেথড, আর private ফিল্ড মিলিয়ে একটা বাস্তব "বুক লাইব্রেরি" ক্লাস বানাই।

class Library {
  #books = []; // প্রাইভেট — বাইরে থেকে সরাসরি পাল্টানো যাবে না

  addBook(title, author) {
    this.#books.push({ title, author, id: Date.now() });
  }

  removeBook(id) {
    this.#books = this.#books.filter((book) => book.id !== id);
  }

  get totalBooks() {
    return this.#books.length;
  }

  get allBooks() {
    return [...this.#books]; // spread দিয়ে কপি — আসল অ্যারে বাইরে থেকে পাল্টানো যাবে না
  }

  static createWithDefaults() {
    const lib = new Library();
    lib.addBook("সোনার তরী", "রবীন্দ্রনাথ ঠাকুর");
    lib.addBook("পথের পাঁচালী", "বিভূতিভূষণ");
    return lib;
  }
}

const myLibrary = Library.createWithDefaults();
console.log(myLibrary.totalBooks); // 2
myLibrary.addBook("চোখের বালি", "রবীন্দ্রনাথ ঠাকুর");
console.log(myLibrary.totalBooks); // 3
console.log(myLibrary.allBooks); // সব বইয়ের অ্যারে

এখানে #books সম্পূর্ণ সুরক্ষিত, আর get allBooks() একটা কপি রিটার্ন করে (আগের পর্বের Spread টেকনিক) — এতে বাইরে থেকে কেউ সরাসরি myLibrary.allBooks.push(...) করলেও আসল #books অপরিবর্তিত থাকে।

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

Exercise:

  1. একটা User ক্লাস বানাও — নাম, ইমেইল প্রপার্টি, আর একটা get initials() getter যেটা নামের প্রথম অক্ষর রিটার্ন করবে।
  2. উপরের Library ক্লাসে একটা findByAuthor(author) মেথড যোগ করো।
  3. BankAccount ক্লাসে একটা withdraw(amount) মেথড যোগ করো, যেটা ব্যালেন্সের বেশি টাকা তুলতে দিলে এরর থ্রো করবে।
  4. একটা static মেথড Book.compareViews(bookA, bookB) বানাও যেটা কোন বইয়ের ভিউ বেশি সেটা রিটার্ন করবে।

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

পরের পর্বে আমরা শিখব OOP — Inheritance — কীভাবে একটা ক্লাস আরেকটা ক্লাসের বৈশিষ্ট্য উত্তরাধিকারসূত্রে পায় (extends, super), যাতে কোড পুনর্ব্যবহার আরও সহজ হয়।

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

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

যোগাযোগ ফর্ম