পাঠ ১৮.১

Object-Oriented language-এর বৈশিষ্ট্য

Characteristics of Object-Oriented Languages

কোন language-কে "object-oriented" বলা যায় তা নিয়ে কোনো universal সংজ্ঞা নেই, তবে সাধারণত OOP language-গুলোর তিনটা বৈশিষ্ট্য থাকে — object, encapsulation, এবং inheritance। এই পাঠে দেখব Rust এই তিনটা ধারণাকে কোথায় কীভাবে support করে।

Object — data এবং behavior একসাথে

Gang of Four-এর Design Patterns বইয়ে সংজ্ঞা — object-oriented program object দিয়ে তৈরি। একটা object data এবং সেই data-র উপর কাজ করা procedure (যাকে method বা operation বলে) — দু'টোই encapsulate করে।

এই সংজ্ঞা অনুসারে — Rust object-oriented। struct আর enum-এ data থাকে, impl block-এ method। নাম "object" না হলেও, functionality একই।

Encapsulation — implementation hide করা

Encapsulation মানে object-এর implementation detail বাইরের কোড থেকে access করা যাবে না — user শুধু public API-র মাধ্যমে interact করবে। এতে internal change করা সম্ভব হয় public interface ভাঙা ছাড়াই।

নিচে AveragedCollection struct — list-এ integer যোগ-বিয়োগ করার সাথে সাথে cached average maintain করে:

src/lib.rsrust
pub struct AveragedCollection {
    list: Vec<i32>,
    average: f64,
}

Struct pub, কিন্তু তার field-গুলো default-এ private। এতে data integrity রক্ষা হয়।

src/lib.rsrust
impl AveragedCollection {
    pub fn add(&mut self, value: i32) {
        self.list.push(value);
        self.update_average();
    }

    pub fn remove(&mut self) -> Option<i32> {
        let result = self.list.pop();
        match result {
            Some(value) => {
                self.update_average();
                Some(value)
            }
            None => None,
        }
    }

    pub fn average(&self) -> f64 {
        self.average
    }

    fn update_average(&mut self) {
        let total: i32 = self.list.iter().sum();
        self.average = total as f64 / self.list.len() as f64;
    }
}
  • শুধু add, remove, average public method-গুলো data পরিবর্তন বা পাঠ করে।
  • list এবং average field private — কেউ বাইরে থেকে ভেঙে দিতে পারবে না।
  • চাইলে ভবিষ্যতে Vec<i32> থেকে HashSet<i32>-এ change করো — public API অপরিবর্তিত থাকবে।

Rust-এ encapsulation enforce হয় pub keyword দিয়ে — module, type, function, এবং method level-এ visibility control।

Inheritance — Rust-এর ভিন্ন approach

Rust traditional inheritance support করে না — অর্থাৎ এক struct অন্য struct-এর field আর method automatically inherit করতে পারে না (macro ছাড়া)। সাধারণত মানুষ inheritance use করে দু'টো কারণে —code reuse এবং polymorphism। দু'টোর জন্যই Rust-এ আলাদা solution আছে।

১. Code reuse — default trait method

Inheritance-এর বদলে trait-এ default implementation দাও — যে type এই trait implement করবে সে method-টা বিনামূল্যে পাবে।

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)") // default implementation
    }
}

চাইলে implementor override-ও করতে পারে — child class parent method override করার মতোই।

২. Polymorphism — trait object আর generic

Polymorphism — runtime-এ একাধিক type substitute করা, যদি তারা common characteristic share করে। Rust-এ দু'রকম:

  • Trait object — runtime polymorphism (dynamic dispatch)।
  • Generic + trait bound — compile-time polymorphism (bounded parametric polymorphism, monomorphization)।

কেন Rust inheritance এড়িয়ে গেছে

  • Inheritance অনেক সময় প্রয়োজনের চেয়ে বেশি code share করে।
  • Subclass-এ এমন characteristic চলে আসে যা সেই subclass-এর জন্য applicable না।
  • Single inheritance restrictive; design কম flexible।
  • Subclass-এ method call হয়ে যেতে পারে যেটা logically সেখানে অর্থ করে না।

এজন্য Rust trait object আর generic ব্যবহার করে — flexible, type-safe, এবং intent পরিষ্কার।

এই পাঠ থেকে যা শিখলে

  • Rust-এ "object" নাম না হলেও struct/enum + impl মিলে data + behavior package করে।
  • pub keyword দিয়ে encapsulation — field-default private, public method-এর মাধ্যমেই access।
  • Traditional inheritance Rust-এ নেই; default trait method-এ code reuse, trait object/generic-এ polymorphism।
  • Inheritance-এর tight coupling এড়াতেই Rust ভিন্ন path বেছেছে — flexibility আর safety দু'টোই বাড়ায়।