OOP Inheritance සිංහලෙන්: Python වලින් Car, SportsCar උදාහරණයක් - SC Guide

OOP Inheritance සිංහලෙන්: Python වලින් Car, SportsCar උදාහරණයක් - SC Guide

කොහොමද යාලුවනේ! අද අපි කතා කරන්න යන්නේ software development වල ගොඩක්ම වැදගත් concept එකක් ගැන – ඒ තමයි OOP හෙවත් Object-Oriented Programming වල 'Inheritance' කියන එක. මේක හරියට පවුලක දරුවෙක්ට අම්මා තාත්තාගෙන් දේපල ලැබෙනවා වගේ වැඩක්. හරි, එහෙනම් මේක මොකද්ද, මොකටද මේක පාවිච්චි කරන්නේ කියලා අපි සරලවම බලමු.

නැත්නම් මෙහෙම හිතන්නකෝ, අපිට Car එකක් ගැන class එකක් හදන්න ඕන කියලා. ඒකට make, model, year වගේ attributes (දේපල) වගේම start_engine(), accelerate() වගේ methods (වැඩ ටික) ගොඩක් එනවා. හැබැයි දැන් අපිට ඕන SportsCar එකක් ගැනත් class එකක් හදන්න. SportsCar එකත් Car එකක්නේ. එතකොට Car එකට තියෙන attributes, methods ඔක්කොම SportsCar එකටත් තියෙනවා.

මෙතනදී අපිට පුළුවන් Car class එකේ තියෙන දේවල් reuse කරලා, SportsCar class එක Car class එකෙන් 'inherit' කරන්න. එතකොට Car එකේ තියෙන දේවල් ආපහු SportsCar එකේ ලියන්න ඕන වෙන්නේ නෑ. වැඩේ ලේසි වෙනවා නේද? අද අපි Python වලින් මේක කොහොමද කරන්නේ කියලා, ලංකාවේ විදියට, සරලව කතා කරමු.

Inheritance කියන්නේ මොකද්ද? (is-a relationship)

Inheritance කියන්නේ OOP වල core pillars හතරෙන් එකක්. (අනිත් තුන තමයි Encapsulation, Polymorphism, Abstraction). සරලවම කිව්වොත්, එක class එකක තියෙන attributes (දේපළ) සහ methods (වැඩ ටික) තව class එකකට උරුම කරගන්න පුළුවන්කම.

මේකට කියනවා 'is-a relationship' එකක් කියලා. උදාහරණයක් විදියට:

  • A 'SportsCar' is a 'Car'.
  • A 'Dog' is an 'Animal'.
  • A 'Smartphone' is a 'Device'.

මේ is-a relationship එක හරියට තේරුම් ගන්න එක Inheritance හරියට පාවිච්චි කරන්න අත්‍යවශ්‍යයි. අපේ SportsCar උදාහරණය ගත්තොත්, SportsCar එක කියන්නේ Car වර්ගයක්. ඒ කියන්නේ, Car එකට තියෙන wheels, engine, doors වගේ දේවල් SportsCar එකටත් තියෙනවා. ඒ වගේම start_engine(), accelerate() වගේ actions (methods) SportsCar එකටත් apply වෙනවා. හැබැයි SportsCar එකට තමන්ටම ආවේණික features (උදා: turbo_boost()) වගේ දේවලුත් තියෙන්න පුළුවන්.

මේ විදියට code reuse කරන්න පුළුවන් වෙන එක, code එක maintain කරන්න ලේසි වෙන එක, scalability වැඩි වෙන එක Inheritance වල ප්‍රධාන වාසි.

Parent සහ Child Classes

Inheritance වලදී class දෙකක් අතර සම්බන්ධයක් හැදෙනවා. මේ class දෙක හඳුන්වන්නේ 'Parent Class' (හෝ Base Class / Super Class) සහ 'Child Class' (හෝ Derived Class / Sub Class) කියලා.

Parent Class:

මේක තමයි වැඩිමහල්ලා. මේ class එකේ තියෙන attributes සහ methods තමයි child class එකට උරුම වෙන්නේ. අපේ උදාහරණයේ Car class එක තමයි Parent Class එක.

Child Class:

මේක තමයි දරුවා. මේ class එක Parent class එකෙන් attributes සහ methods උරුම කරගන්නවා. ඒ වගේම තමන්ටම ආවේණික attributes සහ methods අලුතෙන් එකතු කරගන්නත් පුළුවන්. නැත්නම් Parent class එකේ තියෙන methods වලට වඩා වෙනස් විදියට වැඩ කරන්නත් පුළුවන් (ඒක තමයි Method Overriding).

Python වලදී Child class එකක් හදද්දී Parent class එක නම parentheses ඇතුලේ දාලා define කරනවා. මෙන්න මෙහෙමයි:

class ParentClass:
    # Parent class attributes and methods

class ChildClass(ParentClass):
    # Child class attributes and methods

දැන් Car සහ SportsCar වලින් බලමු:

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.speed = 0

    def start_engine(self):
        return f"{self.make} {self.model} engine started."

    def accelerate(self, amount):
        self.speed += amount
        return f"{self.make} {self.model} accelerating. Current speed: {self.speed} km/h."

class SportsCar(Car): # SportsCar is a Child of Car
    def __init__(self, make, model, year, top_speed):
        super().__init__(make, model, year) # Call Parent's constructor
        self.top_speed = top_speed

    def activate_turbo(self):
        return f"{self.make} {self.model} activating turbo boost! Maximum speed: {self.top_speed} km/h.
"

මෙතන SportsCar class එක Car class එකෙන් inherit කරනවා. ඒ කියන්නේ Car class එකේ තියෙන make, model, year, speed වගේ attributes වගේම start_engine(), accelerate() වගේ methods SportsCar object එකකටත් පාවිච්චි කරන්න පුළුවන්.

Overriding Methods සහ super() Magic එක

කලින් කිව්වා වගේ, Child class එකකට පුළුවන් Parent class එකේ තියෙන method එකක් තමන්ට ඕන විදියට වෙනස් කරන්න. මේකට කියනවා 'Method Overriding' කියලා.

උදාහරණයක් විදියට, සාමාන්‍ය Car එකක් accelerate කරනවට වඩා SportsCar එකක් accelerate කරන විදිය වෙනස් වෙන්න පුළුවන්. එතකොට අපිට පුළුවන් SportsCar class එකේ accelerate() method එක override කරන්න.

Python වලදී Method Overriding කරන්න හරිම ලේසියි. Child class එකේ Parent class එකේ තියෙන method එකම නමින් අලුතෙන් define කරන්න විතරයි තියෙන්නේ. Python interpreter එක child class එකේ method එක detect කරලා, Parent එකේ method එකට වඩා Child එකේ method එක execute කරනවා.

super() function එක:

දැන් පොඩි ප්‍රශ්නයක් එනවා. සමහර වෙලාවට අපිට Child class එකේදී Parent class එකේ method එකේ functionality එකත් ඕන වෙනවා, හැබැයි ඒකට අමතරව තව ටිකක් එකතු කරන්නත් ඕන වෙනවා. එතකොට තමයි super() කියන magic function එක වැඩට එන්නේ.

super() function එකෙන් අපිට Parent class එකේ methods වලට access කරන්න පුළුවන්. විශේෂයෙන්ම Child class එකේ constructor (__init__) එක ඇතුලේ Parent class එකේ constructor එක call කරන්න මේක පාවිච්චි කරනවා.

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.speed = 0
        print(f"Car {make} {model} created.")

    def accelerate(self, amount):
        self.speed += amount
        print(f"{self.make} {self.model} accelerating. Current speed: {self.speed} km/h.")
        
class SportsCar(Car):
    def __init__(self, make, model, year, top_speed):
        super().__init__(make, model, year) # Calling Car's __init__
        self.top_speed = top_speed
        print(f"SportsCar {make} {model} created with top speed {top_speed}.")

    def accelerate(self, amount): # Overriding accelerate method
        if self.speed + amount > self.top_speed:
            print(f"Cannot accelerate beyond top speed {self.top_speed} km/h.")
            self.speed = self.top_speed
        else:
            # Calling Car's accelerate but with boosted amount
            super().accelerate(amount * 2) 
            print(f"SportsCar {self.make} {self.model} accelerating with extra boost!")

මේ උදාහරණයෙන් පේනවා නේද, SportsCar එකේ __init__ එක ඇතුලේ අපි super().__init__(make, model, year) කියලා Parent Class එකේ constructor එක call කරනවා. ඒ වගේම accelerate method එක override කරලා, ඒක ඇතුලෙත් super().accelerate(amount * 2) කියලා Parent Class එකේ accelerate method එක call කරනවා, හැබැයි amount එක දෙගුණයක් කරලා. වැඩේ නියමයි නේද!

ප්‍රායෝගික උදාහරණයක්: SportsCar එකක් හදමු!

දැන් අපි බලමු මේ concepts ටික එකට එකතු කරලා කොහොමද Car සහ SportsCar class හදන්නේ කියලා. මේක ටිකක් දිග code එකක්, හැබැයි කලින් කතා කරපු හැමදේම මේකේ තියෙනවා.

# Parent Class: Car
class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.speed = 0
        print(f"Car {self.make} {self.model} ({self.year}) created.")

    def start_engine(self):
        print(f"{self.make} {self.model}'s engine started. Vroom!")
        return True

    def accelerate(self, amount):
        self.speed += amount
        print(f"{self.make} {self.model} accelerating. Current speed: {self.speed} km/h.")
        return self.speed

    def brake(self):
        self.speed = 0
        print(f"{self.make} {self.model} stopped.")
        return self.speed

    def get_info(self):
        return f"This is a {self.year} {self.make} {self.model}."

# Child Class: SportsCar - Inherits from Car
class SportsCar(Car):
    def __init__(self, make, model, year, top_speed, spoiler_type="adjustable"):
        super().__init__(make, model, year) # Initialize Parent class attributes
        self.top_speed = top_speed
        self.spoiler_type = spoiler_type
        print(f"SportsCar {self.make} {self.model} ({self.year}) created with top speed {self.top_speed} km/h.")

    # Overriding the accelerate method
    def accelerate(self, amount):
        # We can add sports car specific acceleration logic here
        boost_amount = amount * 1.5 # Sports cars accelerate faster!
        
        # Call the parent's accelerate method with the boosted amount
        # This reuses the logic from the Car class
        current_speed = super().accelerate(boost_amount) 
        
        if current_speed >= self.top_speed * 0.8: # Activate turbo if close to max speed
            print(f"  --> Turbo boost engaging for {self.make} {self.model}!")
        return current_speed

    # New method specific to SportsCar
    def engage_sport_mode(self):
        print(f"{self.make} {self.model} engaging sport mode! Spoiler set to {self.spoiler_type}.")
        self.speed_limit = self.top_speed
        return True

    # Overriding get_info method to add SportsCar specific info
    def get_info(self):
        parent_info = super().get_info() # Get parent's info
        return f"{parent_info} It's a SportsCar with a top speed of {self.top_speed} km/h and {self.spoiler_type} spoiler."

# --- Test the classes ---
print("\n--- Creating a normal Car ---")
my_car = Car("Toyota", "Corolla", 2020)
print(my_car.get_info())
my_car.start_engine()
my_car.accelerate(50)
my_car.brake()
print("-" * 30)

print("\n--- Creating a Sports Car ---")
my_sports_car = SportsCar("Ferrari", "488 GTB", 2022, 330, "fixed")
print(my_sports_car.get_info())
my_sports_car.start_engine() # Inherited from Car
my_sports_car.accelerate(80) # Overridden method
my_sports_car.accelerate(120) # Overridden method, approaching top speed
my_sports_car.engage_sport_mode() # SportsCar specific method
my_sports_car.accelerate(100) # Overridden method, going further
my_sports_car.brake() # Inherited from Car
print("-" * 30)

# Demonstrating attribute access
print(f"My sports car make: {my_sports_car.make}") # Inherited attribute
print(f"My sports car top speed: {my_sports_car.top_speed}") # SportsCar specific attribute

මේ code එක run කරලා බලන්න. Car එකටත්, SportsCar එකටත් තියෙන methods කොහොමද වැඩ කරන්නේ කියලා. accelerate() method එකේ වෙනසත්, SportsCar එකට විතරක් තියෙන engage_sport_mode() method එකත් පැහැදිලිව පේනවා නේද?

පොඩි අවුල් සහගත තැන්: MRO සහ super().__init__() අමතක වුණොත්

Inheritance කියන්නේ powerful concept එකක් වුණාට, හරියට පාවිච්චි නොකළොත් පොඩි ප්‍රශ්න ඇතිවෙන්නත් පුළුවන්. විශේෂයෙන්ම, multi-level inheritance (class A -> B -> C) වගේ complicated scenarios වලදී.

MRO (Method Resolution Order):

Python වලදී method එකක් call කරද්දී, interpreter එක class hierarchy එකේ method එක හොයන පිළිවෙලක් තියෙනවා. මේකට තමයි MRO කියන්නේ. සාමාන්‍යයෙන්, Python DLR (Depth-First Left-to-Right) algorithm එකක් පාවිච්චි කරනවා. ClassName.mro() method එකෙන් අපිට MRO එක බලන්න පුළුවන්.

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(D.mro())
# Output: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

මේක තවදුරටත් complicate වෙන්න පුළුවන් Multiple Inheritance වලදී (එක class එකක් class කිහිපයකින් inherit කරනවා නම්). හැබැයි අදට අපි මේ ගැන ගැඹුරට කතා කරන්නේ නෑ, දැනට මෙහෙම දෙයක් තියෙනවා කියලා දැනගෙන හිටියොත් ඇති.

super().__init__() call කරන්න අමතක වුණොත්:

Child class එකක් හදද්දී, __init__ method එක override කරනවා නම්, Parent class එකේ __init__ එකට අදාල attributes initialize කරන්න super().__init__() call කරන්න අමතක කරන්න එපා. නැත්නම් Parent class එකේ attributes Child object එකට initialize වෙන්නේ නෑ, ඒ වෙලාවට AttributeError වගේ errors එන්න පුළුවන්.

class Car:
    def __init__(self, make, model):
        self.make = make
        self.model = model

class SportsCar(Car):
    def __init__(self, make, model, top_speed):
        # super().__init__(make, model) # If this line is commented out...
        self.top_speed = top_speed

# my_sports_car = SportsCar("Porsche", "911", 310)
# print(my_sports_car.make) # This will cause an AttributeError!

මේ වගේ පොඩි වැරදි නිසා debug කරන්න ගොඩක් වෙලා යන්න පුළුවන්. ඒ නිසා super().__init__() call කරන එක අමතක නොකර ඉන්න.

Best Practices: නිකන් Inheritance පාවිච්චි නොකර ඉමු

Inheritance ගොඩක් powerful වුණාට, හැම වෙලාවෙම ඒකම පාවිච්චි කරන්න ඕන නෑ. වැරදි විදියට පාවිච්චි කළොත් code base එක complicate වෙන්න පුළුවන්.

"Is-a" Relationship:

Inheritance පාවිච්චි කරන්න ඕන 'is-a relationship' එකක් තියෙනවා නම් විතරයි. A 'SportsCar' is a 'Car'. A 'Square' is a 'Rectangle'. A 'Dog' is an 'Animal'. මේ වගේ තැන් වලට inheritance නියමයි.

"Has-a" Relationship (Composition):

සමහර වෙලාවට අපිට ඕන වෙන්නේ 'has-a relationship' එකක්. A 'Car' has an 'Engine'. A 'Person' has a 'Heart'. මේ වගේ තැන් වලට Composition (object එකක් ඇතුලේ තව object එකක් instance කරන එක) තමයි හොඳම විසඳුම. Inheritance වලට වඩා Composition preferred වෙන අවස්ථා ගොඩක් තියෙනවා, මොකද ඒකෙන් flexibility එක වැඩි කරනවා.

ඉතින්, inheritance පාවිච්චි කරන්න කලින් දෙපාරක් හිතන්න. ඇත්තටම is-a relationship එකක්ද කියලා. නැත්නම් composition ගැන හිතන එක හොඳයි.

නිගමන

ඔන්න එහෙනම් යාලුවනේ, අද අපි OOP වල Inheritance කියන concept එක ගැන සරලව කතා කළා. Car සහ SportsCar උදාහරණයෙන් මේක කොහොමද Python වලින් implement කරන්නේ කියලත් බැලුවා.

Inheritance කියන්නේ code reuse කරන්න, maintainability වැඩි කරන්න, code base එක organize කරන්න ගොඩක් උදව් වෙන දෙයක්. හැබැයි ඒක හරි තැනට, හරියට පාවිච්චි කරන එක තමයි වැදගත්. super() function එක ගැනත්, Method Overriding ගැනත් දැන් ඔයාලට හොඳ අවබෝධයක් ඇති කියලා හිතනවා.

මේ article එකෙන් අලුත් දෙයක් ඉගෙන ගත්තා කියලා හිතනවා නම්, comment section එකේ ඔයාලගේ අදහස් කියන්න. මේ code snippets ටික ඔයාලගේ computer එකේ run කරලා බලන්න. එතකොට තවත් හොඳට තේරෙයි.

තවත් මේ වගේ programming concepts ගැන සරලව, සිංහලෙන් කතා කරන article එකකින් හමුවෙමු. එතකන් හැමෝටම සුබ දවසක්!

ජයවේවා!