Composition vs Inheritance

The Is-A vs Has-A Relationship

ဒါကတော့ OOP မှာ အရေးပါတဲ့ Design Decision ပါ။ Bro က Class တွေ ဆောက်တဲ့အခါ ဘယ်လို ဆက်စပ်မလဲဆိုတာ ဆုံးဖြတ်ရတယ်။

နည်းလမ်း နှစ်မျိုး ရှိတယ်: Inheritance (အမွေဆက်ခံခြင်း) နဲ့ Composition (အစိတ်အပိုင်းတွေ ပေါင်းစပ်ခြင်း)။

1. Inheritance (အမွေဆက်ခံခြင်း) - The Is-A Relationship

ဒါကတော့ Bro သိပြီးသားဖြစ်မယ့် Parent နဲ့ Child ဆက်ဆံရေးမျိုးပဲ။

Logic: ငါက မင်းရဲ့ သွေးသားပဲ၊ မင်းတတ်တာ အကုန် ငါတတ်တယ်၊ ပြီးမှ ငါ့ဟာငါ အသစ်ထပ်သင်မယ် ဆိုတဲ့ သဘော။

Keyword: Is-A (ဥပမာ - Dog is an Animal).

အားသာချက်:

  • Code တွေကို ထပ်ခါထပ်ခါ မရေးရတော့ဘူး (DRY - Don't Repeat Yourself)။

အားနည်းချက်:

  • Tight Coupling ဖြစ်တယ်။ ဆိုလိုတာက Parent Class မှာ တစ်ခုခု ပြင်လိုက်ရင် Child Class တွေမှာပါ လိုက်ပြီး Error တက်တာ၊ ပြောင်းလဲသွားတာတွေ ဖြစ်နိုင်တယ်။ ပြင်ရခက်တယ်။

ဥပမာ (Real World):

Bro က Human ဆိုတဲ့ Class ထဲကနေ ဆင်းသက်လာတဲ့ Programmer ဆိုပါစို့။ Human လုပ်တတ်တာမှန်သမျှ (စားတယ်၊ အိပ်တယ်) Bro လည်း လုပ်တတ်တယ်။ ပြီးမှ Code ရေးတာ ထပ်ဖြည့်ထားတာ။

Inheritance Example
class Human: def sleep(self): print("zzz..") class Programmer(Human): # Programmer က Human ဆီက အမွေယူလိုက်တာ def code(self): print("Writing the Python codes") # Programmer is a Human

2. Composition (အစိတ်အပိုင်းတွေ ပေါင်းစပ်ခြင်း) - The Has-A Relationship

ဒါကတော့ ဒီနေ့ခေတ် ဆရာကြီးတွေ ပိုကြိုက်တဲ့ နည်းလမ်းပေါ့။ Lego ဆက်သလိုမျိုး။

Logic: ငါက မင်းရဲ့ သား မဟုတ်ဘူး၊ ဒါပေမယ့် ငါ့ဆီမှာ မင်းကို Component(အစိတ်အပိုင်း) တစ်ခုအနေနဲ့ ထည့်သုံးထားတယ် ဆိုတဲ့ သဘော။

Keyword: Has-A (ဥပမာ - Car has an Engine).

အားသာချက်:

  • Loose Coupling ဖြစ်တယ်။ လိုချင်တဲ့ အစိတ်အပိုင်းကို ဖြုတ်တပ်ရတာ၊ အသစ်လဲရတာ အရမ်းလွယ်တယ်။ Engine ပျက်ရင် ကားတစ်စီးလုံး ဖျက်စရာမလိုဘူး၊ Engine လဲလိုက်ရုံပဲ။

အားနည်းချက်:

  • Code နည်းနည်း ပိုရေးရနိုင်တယ်။

ဥပမာ (Real World):

Bro မှာ Laptop တစ်လုံးရှိတယ်။ Laptop က Computer Class ကနေ မွေးဖွားလာတာ မဟုတ်ဘူး။ Laptop ထဲမှာ CPU ရှိမယ်၊ RAM ရှိမယ်၊ Battery ရှိမယ်။ အဲ့ဒါတွေကို ပေါင်းစပ် (Compose) လုပ်ထားတာ။

Composition Example
class Engine: def start(self): print("Vroom! Engine နိုးပြီ") class Car: def __init__(self): self.engine = Engine() # Car ထဲမှာ Engine ကို ပိုင်ဆိုင်ထားတာ (Has-A) def drive(self): self.engine.start() # Engine ကို လှမ်းခိုင်းလိုက်တာ print("ကားမောင်းထွက်ပြီ...") # Car has an Engine

The Battle: ဘယ်ဟာကို ရွေးမလဲ?

ဒီနေရာမှာ အရေးကြီးဆုံး Design Principle တစ်ခုကို မှတ်ထားပေးပါ။

အရေးကြီးဆုံး Principle: Favor Composition over Inheritance (Inheritance ထက် Composition ကို ဦးစားပေး သုံးပါ)

ဘာလို့လဲဆိုတော့...

Inheritance က Rigid (တောင့်တင်း) တယ်:

Bro က ဂိမ်းရေးတယ်ဆိုပါစို့။ Bird (ငှက်) ဆိုတဲ့ Class ဆောက်တယ်။ အဲ့အောက်မှာ Eagle (လင်းယုန်) နဲ့ Penguin (ပင်ဂွင်း) ထားလိုက်မယ်။ Bird Class မှာ fly() (ပျံမယ်) ဆိုတဲ့ function ထည့်လိုက်ရင်... ပြဿနာက ပင်ဂွင်းက မပျံတတ်ဘူးလေ။ အဲ့တော့ ပင်ဂွင်းက မလိုအပ်ဘဲ fly() ကို အမွေရနေရော။ Code တွေ ရှုပ်ကုန်ရော။

Composition က Flexible (ပြောင်းလွယ်ပြင်လွယ်) ဖြစ်တယ်:

ဒီတော့ Composition နဲ့ စဉ်းစားမယ်။ Penguin class ထဲမှာ SwimmingSkill ဆိုတဲ့ Class ကို ထည့်ပေးလိုက်မယ်။ Eagle class ထဲမှာ FlyingSkill ကို ထည့်မယ်။ လိုချင်တာကိုပဲ ရွေးထည့် (Compose) လုပ်လို့ရသွားပြီ။ ဘယ်လောက် မိုက်လဲ!

အနှစ်ချုပ်

အခြေအနေ ဘာသုံးမလဲ? ဘာလို့လဲ?
Is-A (အမျိုးအစားတူရင်) Inheritance ဥပမာ - Cat is an Animal. (ကြောင်ဟာ တိရစ္ဆာန်အမျိုးအစားမို့လို့)
Has-A (ပိုင်ဆိုင်ရင်) Composition ဥပမာ - User has a Database. (User က Database အမျိုးအစား မဟုတ်ဘူး၊ Database ကို သုံးမှာမို့လို့)
Behavior တွေ ကွဲပြားရင် Composition ဥပမာ - ငှက်တိုင်း မပျံတတ်ဘူး၊ ငါးတိုင်း ရေမကူးတတ်ဘူး ဆိုရင် Skill တွေကို ခွဲထုတ်ပြီး ပေါင်းစပ်တာ ပိုကောင်းတယ်။

နိဂုံး

ကဲ... ဒါကတော့ Composition vs Inheritance ရဲ့ အနှစ်ချုပ်ပါပဲ။

သတိ: Inheritance က မကောင်းဘူးလို့ ပြောတာမဟုတ်ဘူးနော်၊ သူ့နေရာနဲ့သူ သုံးရင် ကောင်းတယ်။ ဒါပေမယ့် Composition က ပိုပြီး Safe ဖြစ်ပြီး ပြင်ရလွယ်တာမို့ အလုပ်ခွင်မှာ ပိုသုံးကြတယ်။
  • Inheritance: Is-A relationship, Code reusability ကောင်းတယ်, ဒါပေမယ့် Tight Coupling ဖြစ်တယ်။
  • Composition: Has-A relationship, Flexible ဖြစ်တယ်, Loose Coupling ဖြစ်တယ်, အလုပ်ခွင်မှာ ပိုသုံးကြတယ်။
  • Suggestion: Favor Composition over Inheritance!