Polymorphism (ပုံစံ အမျိုးမျိုး ယူနိုင်ခြင်း)

B3.2 (HL): Implement polymorphism using parent-type references and method overriding.
မြန်မာလို: parent type reference (object ရှိရာ memory နေရာကို ညွှန်းတဲ့ တန်ဖိုး) တွေနဲ့ method overriding (parent ရဲ့ method ကို child က အစားထိုး ရေးခြင်း) ကို သုံးပြီး polymorphism (ပုံစံ အမျိုးမျိုး ယူနိုင်ခြင်း) ကို code နဲ့ ကိုယ်တိုင် ရေးနိုင်ရပါမယ်။

ဒီစာမျက်နှာက အကြောင်းအရာတွေကို HL အဆင့်မှာပဲ စစ်ဆေးပါတယ်။ SL (Standard Level) နဲ့ HL (Higher Level) ဆိုတာ IB Computer Science (နိုင်ငံတကာ ကျောင်းတွေမှာ သင်တဲ့ ကွန်ပျူတာသိပ္ပံ သင်ရိုး) သင်တန်းရဲ့ အဆင့်နှစ်ဆင့်ဖြစ်ပြီး HL အပိုင်းတွေက ပိုနက်နက်ရှိုင်းရှိုင်း လေ့လာရတဲ့ အပိုင်းတွေပါ။


အဓိက အယူအဆများ

Polymorphism ဆိုတာ ဘာလဲ

Polymorphism ဆိုတာ “ပုံစံ အများ” (many forms) လို့ အဓိပ္ပာယ်ရပါတယ်။ OOP (object တွေကို အခြေခံပြီး program ရေးနည်း) မှာတော့ parent type ဖြစ်တဲ့ variable (တန်ဖိုး သိမ်းထားတဲ့ နာမည်ပေးထားတဲ့ နေရာ) တစ်ခုတည်းက မတူတဲ့ child type တွေရဲ့ object (class တစ်ခုကနေ ဖန်တီးထားတဲ့ instance) တွေကို ညွှန်းနိုင်ပြီး method (သီးခြား အလုပ်တစ်ခု လုပ်ပေးတဲ့ code အပိုင်း) တစ်ခုကို ခေါ်တဲ့အခါ Java က ကြေညာထားတဲ့ variable type ကို မဟုတ်ဘဲ object ရဲ့ တကယ့် type ပေါ် မူတည်ပြီး မှန်ကန်တဲ့ version ကို အလိုအလျောက် run ပေးတယ်လို့ ဆိုလိုပါတယ်။

Animal a1 = new Dog("Max", 3, "Labrador");
Animal a2 = new Cat("Whiskers", 5);
Animal a3 = new Animal("Generic", 1);

System.out.println(a1.speak());  // Woof!  (Dog's version)
System.out.println(a2.speak());  // Meow!  (Cat's version)
System.out.println(a3.speak());  // ...    (Animal's version)
# Python variables have no declared type, so polymorphism needs no special syntax:
# any variable can hold any object, and the OBJECT decides which speak() runs
a1 = Dog("Max", 3, "Labrador")
a2 = Cat("Whiskers", 5)
a3 = Animal("Generic", 1)

print(a1.speak())    # Woof!  (Dog's version)
print(a2.speak())    # Meow!  (Cat's version)
print(a3.speak())    # ...    (Animal's version)

Python code ထဲက မှတ်ချက်အရ Python variable တွေမှာ ကြေညာထားတဲ့ type မရှိလို့ polymorphism အတွက် အထူး ရေးထုံး မလိုပါဘူး: ဘယ် variable မဆို ဘယ် object ကိုမဆို သိမ်းနိုင်ပြီး ဘယ် speak() ကို run မလဲဆိုတာ object က ဆုံးဖြတ်ပါတယ်။

Variable သုံးခုလုံးကို Animal အဖြစ် ကြေညာထားပေမယ့် တစ်ခုစီက မတူတဲ့ speak() method ကို ခေါ်ပါတယ်။ ဘယ် version ကို run မလဲဆိုတာ Java က runtime (program run နေစဉ်) မှာ တကယ့် object ပေါ် မူတည်ပြီး ဆုံးဖြတ်ပါတယ်: ဒါကို dynamic dispatch (program run နေစဉ် ဘယ် method ကို run မလဲ ဆုံးဖြတ်ခြင်း) ဒါမှမဟုတ် runtime polymorphism လို့ ခေါ်ပါတယ်။

အဓိက နားလည်ရမယ့် အချက်: parent type variable တစ်ခုက ဘယ် child type object ကိုမဆို သိမ်းနိုင်ပါတယ်။ Method ကို ခေါ်တဲ့အခါ child မှာ override (parent ရဲ့ method ကို child က အစားထိုး ရေး) လုပ်ထားတဲ့ version ရှိရင် Java က အဲဒါကို run ပါတယ်။ ဒါကြောင့် polymorphism က အစွမ်းထက်ပါတယ်: ယေဘုယျ type နဲ့ အလုပ်လုပ်တဲ့ code ကို ရေးလိုက်ရုံနဲ့ အဲဒီ code က သီးခြား type တွေနဲ့ ကိုက်ညီအောင် အလိုအလျောက် အလုပ်လုပ်ပါတယ်။

Polymorphic Arrays (မတူတဲ့ subclass တွေရဲ့ object တွေကို အတူ သိမ်းထားတဲ့ array များ)

Polymorphism ရဲ့ အစွမ်းအထက်ဆုံး အသုံးချပုံက child object အမျိုးမျိုးကို ရောပြီး သိမ်းထားတဲ့ parent type array (အရွယ်အစား ပုံသေ၊ အမျိုးအစားတူ ဒေတာစု) တစ်ခု ဖန်တီးတာပါ:

Animal[] zoo = new Animal[4];
zoo[0] = new Dog("Max", 3, "Labrador");
zoo[1] = new Cat("Whiskers", 5);
zoo[2] = new Dog("Buddy", 2, "Beagle");
zoo[3] = new Cat("Luna", 4);

// One loop, handles ALL animal types
for (int i = 0; i < zoo.length; i++) {
    System.out.println(zoo[i].getName() + " says " + zoo[i].speak());
}
zoo = []
zoo.append(Dog("Max", 3, "Labrador"))
zoo.append(Cat("Whiskers", 5))
zoo.append(Dog("Buddy", 2, "Beagle"))
zoo.append(Cat("Luna", 4))

# One loop, handles ALL animal types (a Python list mixes types freely)
for i in range(len(zoo)):
    print(zoo[i].get_name(), "says", zoo[i].speak())

Code ထဲက မှတ်ချက်က loop တစ်ခုတည်းက animal type အားလုံးကို ကိုင်တွယ်တယ်လို့ ဆိုလိုပါတယ် (Python list ကတော့ type အမျိုးမျိုးကို လွတ်လပ်စွာ ရောထားနိုင်ပါတယ်)။

Output (program က ထုတ်ပြတဲ့ ရလဒ်):

Max says Woof!
Whiskers says Meow!
Buddy says Woof!
Luna says Meow!

Loop (ထပ်ခါထပ်ခါ run တဲ့ code အပိုင်း) က element (အစုထဲက အချက်) တစ်ခုစီက Dog လား Cat လား သိစရာ မလိုပါဘူး။ speak() ကို ခေါ်လိုက်ရုံနဲ့ Java က မှန်ကန်တဲ့ version ဆီ ပို့ပေးပါတယ်။ နောက်ပိုင်းမှာ “Tweet!” ကို ပြန်ပေးတဲ့ speak() ပါတဲ့ Bird class တစ်ခု ထပ်ထည့်ရင်လည်း loop က ဘာမှ ပြင်စရာ မလိုဘဲ အလုပ်လုပ်ပါတယ်။

Polymorphism ဘာကြောင့် အရေးကြီးလဲ

Polymorphism မရှိရင် animal type တစ်ခုစီအတွက် သီးခြား array တွေနဲ့ သီးခြား loop တွေ လိုပါလိမ့်မယ်:

// WITHOUT polymorphism -- fragile and repetitive
Dog[] dogs = new Dog[10];
Cat[] cats = new Cat[10];

for (int i = 0; i < dogs.length; i++) {
    if (dogs[i] != null) System.out.println(dogs[i].speak());
}
for (int i = 0; i < cats.length; i++) {
    if (cats[i] != null) System.out.println(cats[i].speak());
}
// Add a Bird? Need a THIRD array and a THIRD loop.
# WITHOUT polymorphism -- fragile and repetitive
dogs = []
cats = []
# fill each list with only its own type...

for i in range(len(dogs)):
    print(dogs[i].speak())
for i in range(len(cats)):
    print(cats[i].speak())
# Add a Bird? Need a THIRD list and a THIRD loop.

Polymorphism နဲ့ဆိုရင်:

// WITH polymorphism -- one array, one loop, works for ALL types
Animal[] animals = new Animal[30];
// Fill with any mix of Dogs, Cats, Birds...

for (int i = 0; i < animals.length; i++) {
    if (animals[i] != null) {
        System.out.println(animals[i].speak());
    }
}
// Add a Bird? Just create Bird objects. The loop ALREADY works.
# WITH polymorphism -- one list, one loop, works for ALL types
animals = []
# Fill with any mix of Dogs, Cats, Birds...

for i in range(len(animals)):
    print(animals[i].speak())
# Add a Bird? Just create Bird objects. The loop ALREADY works.

Code ထဲက မှတ်ချက်တွေက ဒီလို ဆိုလိုပါတယ်: polymorphism မပါရင် code က ပျက်လွယ်ပြီး ထပ်ခါထပ်ခါ ရေးရတယ်၊ Bird ထည့်ရင် တတိယ array နဲ့ တတိယ loop လိုတယ်။ Polymorphism ပါရင် array တစ်ခု၊ loop တစ်ခုနဲ့ type အားလုံးအတွက် အလုပ်လုပ်တယ်၊ Bird ထည့်ရင် Bird object တွေ ဖန်တီးရုံပါပဲ၊ loop က အလုပ်လုပ်ပြီးသား ဖြစ်တယ်။

Polymorphism ရဲ့ အားသာချက်များ:

  • Code reusability (code ကို ပြန်သုံးနိုင်ခြင်း): method ဒါမှမဟုတ် loop တစ်ခုတည်းက subtype (အမွေဆက်ခံထားတဲ့ type) အားလုံးအတွက် အလုပ်လုပ်ပါတယ်
  • Extensibility (ဆက်ချဲ့ရလွယ်ခြင်း): ရှိပြီးသား code ကို မပြင်ဘဲ child class အသစ်တွေ ထည့်နိုင်ပါတယ်
  • Flexibility (ပြောင်းလွယ်မှု): variable တစ်ခုတည်းက အချိန် မတူတဲ့အခါ မတူတဲ့ object type တွေကို သိမ်းနိုင်ပါတယ်
  • Simplicity (ရိုးရှင်းမှု): code ကို ယေဘုယျ type အတွက် ရေးလို့ ရှုပ်ထွေးမှု လျော့ပါတယ်

အလုပ်လုပ်ပုံ: Compile Time နဲ့ Runtime

Animal a = new Dog("Max", 3, "Labrador");
a.speak();       // Which speak() runs?
// a.getBreed(); // Does this compile?
a = Dog("Max", 3, "Labrador")
a.speak()        # Woof! - the actual object decides, exactly as in Java
a.get_breed()    # also works - Python has no declared type, so no compile-time restriction
# Python only checks at RUNTIME: if the object has the method, the call succeeds;
# if it does not, Python raises AttributeError when the line runs

Java code ထဲက မှတ်ချက်တွေက ဘယ် speak() run မလဲ၊ a.getBreed() က compile ဖြစ်မလား လို့ မေးထားတာပါ။ Python code ထဲက မှတ်ချက်တွေအရ Python မှာ ကြေညာထားတဲ့ type မရှိလို့ compile time ကန့်သတ်ချက် မရှိပါဘူး: a.get_breed() လည်း အလုပ်လုပ်ပါတယ်။ Python က RUNTIME မှာပဲ စစ်ပါတယ်: object မှာ အဲဒီ method ရှိရင် ခေါ်မှု အောင်မြင်ပြီး မရှိရင် အဲဒီ line ကို run တဲ့အခါ AttributeError ကို raise (exception ဖြစ်ကြောင်း အချက်ပြ) လုပ်ပါတယ်။

Compile time (compile လုပ်ချိန်) မှာ Java က ကြေညာထားတဲ့ type (Animal) ကို စစ်ပါတယ်။ Compiler (code တစ်ခုလုံးကို ကြိုတင် ဘာသာပြန်တဲ့ program) က Animal မှာ သတ်မှတ်ထားတဲ့ method တွေကိုပဲ ခွင့်ပြုပါတယ်။ Animal မှာ speak() ရှိလို့ ဒီခေါ်မှုက compile ဖြစ်ပါတယ်။ ဒါပေမယ့် Animal မှာ getBreed() မရှိပါဘူး။ ဒါကြောင့် တကယ့် object က Dog ဖြစ်နေရင်တောင် a.getBreed() က compile error (compile လုပ်ချိန်မှာ ဖြစ်တဲ့ error) ဖြစ်ပါလိမ့်မယ်။

Runtime မှာတော့ Java က object ရဲ့ တကယ့် type (Dog) ကို စစ်ပါတယ်။ Dog က speak() ကို override လုပ်ထားတာကို တွေ့လို့ Dog.speak() ကို run ပါတယ်: “…” အစား “Woof!” ကို ပြန်ပေးပါတယ်။

  Java က ဘာကို စစ်လဲ ရလဒ်
Compile time ကြေညာထားတဲ့ type (Animal) Animal method တွေကိုပဲ ခွင့်ပြုတယ်
Runtime Object ရဲ့ တကယ့် type (Dog) Override လုပ်ထားတဲ့ method တွေက child ရဲ့ version ကို သုံးတယ်

Parent type variable တစ်ခုက parent class ထဲမှာ သတ်မှတ်ထားတဲ့ method တွေကိုပဲ ခေါ်နိုင်ပါတယ်။ Object က တကယ်တော့ Dog ဖြစ်နေရင်တောင် Animal variable ကနေတစ်ဆင့် getBreed() ကို ခေါ်လို့ မရပါဘူး။ Cast (type ပြောင်း) လုပ်ဖို့ လိုပါလိမ့်မယ်: ((Dog) a).getBreed()။ ဒါပေမယ့် cast ကို နည်းနည်းပဲ သုံးသင့်ပါတယ်: polymorphism ရဲ့ ရည်ရွယ်ချက်ကို ပျက်စေလို့ပါ။ (Python မှာ cast လုံးဝ မလိုပါဘူး: object မှာ အဲဒီ method တကယ် ရှိရင် a.get_breed() က ရိုးရိုးပဲ အလုပ်လုပ်ပါတယ်။)

Racer ဥပမာ

Inheritance စာမျက်နှာက class hierarchy (class တွေကို အဆင့်ဆင့် ဖွဲ့ထားတဲ့ ပုံစံ) ကို သုံးပြီး polymorphism အလုပ်လုပ်ပုံကို ပြတဲ့ နာမည်ကြီး ဥပမာ တစ်ခုပါ:

Racer[] race = new Racer[2];
race[0] = new Hare("Harry");
race[1] = new Tortoise("Terry");

// Run 100 rounds
for (int round = 0; round < 100; round++) {
    for (int i = 0; i < race.length; i++) {
        race[i].move();  // each racer moves differently
    }
}

// Check positions
for (int i = 0; i < race.length; i++) {
    System.out.println(race[i].getName() + ": " + race[i].getPosition());
}
race = []
race.append(Hare("Harry"))
race.append(Tortoise("Terry"))

# Run 100 rounds
for round_num in range(100):    # not "round" - that name is a Python built-in function
    for i in range(len(race)):
        race[i].move()    # each racer moves differently

# Check positions
for i in range(len(race)):
    print(race[i].get_name() + ": " + str(race[i].get_position()))

Code ထဲက မှတ်ချက်တွေက အလှည့် 100 ပတ် run တယ်၊ ပြိုင်ပွဲဝင် တစ်ယောက်စီ မတူတဲ့ ပုံစံနဲ့ ရွှေ့တယ်၊ ပြီးရင် နေရာတွေကို စစ်တယ်လို့ ဆိုလိုပါတယ်။ Python မှာ round က built-in function (Python မှာ အသင့်ပါတဲ့ function) ဖြစ်လို့ variable နာမည်ကို round_num လို့ ပေးထားပါတယ်။

Hare (ယုန်) ရဲ့ move() က အချိန်ရဲ့ 30% မှာ 5 လှမ်း ခုန်ပြီး 70% မှာ အိပ်နေပါတယ်။ Tortoise (လိပ်) ရဲ့ move() က အမြဲ 1 လှမ်း ရှေ့တိုးပါတယ်။ Loop က တစ်ယောက်စီပေါ်မှာ move() ကို ခေါ်ပါတယ်: polymorphism က ပြိုင်ပွဲဝင် တစ်ယောက်စီအတွက် မှန်ကန်တဲ့ behaviour (object က လုပ်နိုင်တာတွေ) run အောင် သေချာစေပါတယ်။ တစ်ခါတလေ လိပ်က နိုင်ပါတယ်။

Static Polymorphism: Overloading (နာမည်တူပြီး parameter မတူတဲ့ method များ)

အပေါ်က အားလုံးက dynamic polymorphism (program run နေစဉ် ဆုံးဖြတ်တဲ့ polymorphism) ပါ: method ကို override လုပ်ထားရင် run မယ့် implementation (တကယ် အလုပ်လုပ်အောင် ရေးထားတဲ့ code) ကို program run နေစဉ်မှာ တကယ့် object ကနေ ရွေးပါတယ်။ Java မှာ ဒုတိယ ပုံစံ တစ်ခု ရှိပါတယ်: static polymorphism (compile time မှာ ဆုံးဖြတ်တဲ့ polymorphism) ပါ၊ အဲဒါက method overloading ပါ: class တစ်ခုမှာ နာမည်တူပြီး parameter (method ကို ပေးလိုက်တဲ့ တန်ဖိုးကို လက်ခံတဲ့ variable) list မတူတဲ့ method အများ ရှိတာပါ (နှစ်ခုကို Overriding vs Overloading မှာ နှိုင်းယှဉ်ကြည့်ပါ)။ ခေါ်မှု တစ်ခုစီအတွက် compiler က argument (method ကို ခေါ်တဲ့အခါ တကယ် ပေးလိုက်တဲ့ တန်ဖိုး) တွေရဲ့ အရေအတွက်နဲ့ type တွေကို ကြည့်ပြီး ဒီ version တွေထဲက ဘယ်ဟာကို ခေါ်တာလဲ ရွေးပါတယ်။

ဒီ ရွေးချယ်မှု နှစ်ခုက သီးခြားစီပါ။ ခေါ်မှု တစ်ခုက ဘယ် parameter list ပါတဲ့ ဘယ် version ကို သုံးမလဲဆိုတာ compiler က သတ်မှတ်ပါတယ်။ Subclass (အမွေဆက်ခံတဲ့ child class) က အဲဒီ version ကို override လုပ်ထားရင် run နေတဲ့ program က အပေါ်မှာ ရှင်းပြခဲ့သလို တကယ့် object ကနေ subclass ရဲ့ implementation ကို ဆက်ပြီး ရွေးပါတယ်။

Overloading က code ကို ရေးရ၊ ထိန်းသိမ်းရ ပိုထိရောက်အောင် နည်း သုံးမျိုးနဲ့ လုပ်ပေးပါတယ်:

  1. နာမည် တစ်ခုတည်းကို တသမတ်တည်း သုံးခြင်း။ ခေါ်တဲ့သူတွေက input (program ထဲ ထည့်ပေးတဲ့ ဒေတာ) အမျိုးအစား တစ်ခုစီအတွက် method နာမည် မတူတာတွေကို မှတ်ထားစရာ မလိုဘဲ အချိန် အမျိုးအစား အားလုံးအတွက် formatTime ကိုပဲ သုံးပါတယ်။
  2. Argument တွေကို compile time မှာ စစ်ခြင်း။ formatTime("two") လို ဘယ် version နဲ့မှ မကိုက်တဲ့ ခေါ်မှုက compile error ဖြစ်လို့ program မ run ခင်မှာပဲ အမှားကို တွေ့ပါတယ်။
  3. Implementation တစ်ခုကို မျှသုံးခြင်း။ Version တစ်ခုက တကယ့် အလုပ်ကို လုပ်ပြီး ကျန်တဲ့ version တွေက သူတို့ရဲ့ input ကို ပြောင်းပြီး အဲဒီ version ကို ခေါ်ပါတယ်။ အမှား ပြင်တာ ဒါမှမဟုတ် output ကို ပြောင်းတာကို copy သုံးခုမှာ မဟုတ်ဘဲ နေရာ တစ်ခုတည်းမှာ လုပ်ရပါတယ်။
public class TimeFormatter {
    // The formatting logic is written once, in this version
    public String formatTime(int totalSeconds) {
        int minutes = totalSeconds / 60;
        int seconds = totalSeconds % 60;
        return minutes + " min " + seconds + " s";
    }

    // A stopwatch time with a fraction of a second: drop the fraction, then reuse the version above
    public String formatTime(double totalSeconds) {
        return formatTime((int) totalSeconds);
    }

    // A time given as minutes and seconds: convert it, then reuse the first version
    public String formatTime(int minutes, int seconds) {
        return formatTime(minutes * 60 + seconds);
    }

    public static void main(String[] args) {
        TimeFormatter tf = new TimeFormatter();
        System.out.println(tf.formatTime(125));     // int version
        System.out.println(tf.formatTime(61.6));    // double version
        System.out.println(tf.formatTime(3, 75));   // two-int version
    }
}
class TimeFormatter():
    # Python has no overloading: a second def format_time would replace the first.
    # One method takes whole numbers and decimals, because Python does not check argument types
    def format_time(self, total_seconds):
        total = int(total_seconds)    # drops any fraction, as the Java cast does
        minutes = total // 60
        seconds = total % 60
        return str(minutes) + " min " + str(seconds) + " s"

    # In this example the two-input version has its own name
    def format_minutes_seconds(self, minutes, seconds):
        return self.format_time(minutes * 60 + seconds)

tf = TimeFormatter()
print(tf.format_time(125))
print(tf.format_time(61.6))
print(tf.format_minutes_seconds(3, 75))

Java code ထဲက မှတ်ချက်တွေက ဒီလို ဆိုလိုပါတယ်: ပထမ version (int) မှာ format လုပ်တဲ့ logic ကို တစ်ကြိမ်ပဲ ရေးထားတယ်၊ ဒုတိယ version (double) က stopwatch (အချိန်မှတ် နာရီ) အချိန်ရဲ့ ဒသမ စက္ကန့်ကို ဖြုတ်ပြီး အပေါ်က version ကို ပြန်သုံးတယ်၊ တတိယ version (int နှစ်ခု) က မိနစ်နဲ့ စက္ကန့်ကို စက္ကန့်အဖြစ် ပြောင်းပြီး ပထမ version ကို ပြန်သုံးတယ်။ Python code ထဲက မှတ်ချက်တွေကတော့ Python မှာ overloading မရှိလို့ ဒုတိယ def format_time က ပထမ တစ်ခုကို အစားထိုးသွားမယ်၊ Python က argument type တွေကို မစစ်လို့ method တစ်ခုတည်းက ကိန်းပြည့်ရော ဒသမ ကိန်းပါ လက်ခံတယ်၊ int() က Java ရဲ့ cast လိုပဲ ဒသမ အပိုင်းကို ဖြုတ်တယ်၊ input နှစ်ခု version ကို ဒီ ဥပမာမှာ သီးခြား နာမည် ပေးထားတယ်လို့ ဆိုလိုပါတယ်။

Output:

2 min 5 s
1 min 1 s
4 min 15 s

Overloading မရှိရင် ခေါ်တဲ့ နည်း သုံးမျိုးအတွက် နာမည် မတူတာတွေ လိုပါလိမ့်မယ် (ဥပမာ formatSeconds၊ formatDecimalSeconds၊ formatMinutesSeconds)။ အဲဒီ method တွေကလည်း implementation တစ်ခုကို မျှသုံးနိုင်သေးတာမို့ overloading က ထပ်ပေးတာက နာမည် တစ်ခုတည်း ဖြစ်ခြင်းပါပဲ။ Python မှာ overloading မရှိပါဘူး။ Python method တစ်ခုက ကိန်းပြည့်ရော ဒသမ ကိန်းပါ လက်ခံနိုင်ပြီး input နှစ်ခု version ကို သီးခြား နာမည် ပေးတာက ဒီ ဥပမာမှာ ရွေးထားတဲ့ design ပါ: Python မှာ method တစ်ခုကို မထည့်လည်း ရတဲ့ ဒုတိယ parameter တစ်ခု ပေးလို့လည်း ရပါတယ်။

Language နှစ်ခုလုံး ရလဒ် တူအောင် version နှစ်ခုလုံးက စက္ကန့်ရဲ့ ဒသမ အပိုင်းကို round (အနီးဆုံး ကိန်းပြည့်ကို ယူ) မလုပ်ဘဲ ဖြုတ်ပစ်ပါတယ်။ Round လုပ်ရင် .5 နဲ့ ဆုံးတဲ့ တန်ဖိုးတွေမှာ ရလဒ် ကွဲသွားပါလိမ့်မယ်: Java ရဲ့ Math.round(62.5) က 63 ရပေမယ့် Python ရဲ့ round(62.5) ကတော့ 62 ရပါတယ်၊ ဘာလို့လဲဆိုတော့ Python က .5 ကို အနီးဆုံး စုံကိန်းဆီ round လုပ်လို့ပါ။ သုည ဒါမှမဟုတ် ပိုများတဲ့ လက်တွေ့ကျတဲ့ အချိန် တန်ဖိုး ဘယ်ဟာအတွက်မဆို Java နဲ့ Python version တွေက ရလဒ် အတူတူ print ထုတ်ပါတယ်။


အဆင့်ဆင့် ရှင်းပြထားတဲ့ ဥပမာများ

ဥပမာ 1: လစာ ပေးချေမှု တွက်ချက်ခြင်း

public class Employee {
    private String name;
    private double baseSalary;

    public Employee(String name, double baseSalary) {
        this.name = name;
        this.baseSalary = baseSalary;
    }

    public String getName() { return name; }

    public double calculatePay() {
        return baseSalary;
    }
}

public class Manager extends Employee {
    private double bonus;

    public Manager(String name, double baseSalary, double bonus) {
        super(name, baseSalary);
        this.bonus = bonus;
    }

    @Override
    public double calculatePay() {
        return super.calculatePay() + bonus;
    }
}

public class SalesPerson extends Employee {
    private double commissionRate;
    private double totalSales;

    public SalesPerson(String name, double baseSalary, double rate, double sales) {
        super(name, baseSalary);
        this.commissionRate = rate;
        this.totalSales = sales;
    }

    @Override
    public double calculatePay() {
        return super.calculatePay() + (commissionRate * totalSales);
    }
}
class Employee():
    def __init__(self, name, base_salary):
        self._name = name
        self._base_salary = base_salary

    def get_name(self):
        return self._name

    def calculate_pay(self):
        return self._base_salary

class Manager(Employee):
    def __init__(self, name, base_salary, bonus):
        super().__init__(name, base_salary)    # call the parent constructor
        self._bonus = bonus

    def calculate_pay(self):
        return super().calculate_pay() + self._bonus

class SalesPerson(Employee):
    def __init__(self, name, base_salary, rate, sales):
        super().__init__(name, base_salary)
        self._commission_rate = rate
        self._total_sales = sales

    def calculate_pay(self):
        return super().calculate_pay() + (self._commission_rate * self._total_sales)
// Polymorphic array -- one type, three behaviours
Employee[] staff = new Employee[3];
staff[0] = new Employee("Alice", 50000);
staff[1] = new Manager("Bob", 60000, 10000);
staff[2] = new SalesPerson("Charlie", 40000, 0.05, 200000);

double totalPayroll = 0;
for (int i = 0; i < staff.length; i++) {
    double pay = staff[i].calculatePay();
    System.out.println(staff[i].getName() + ": $" + pay);
    totalPayroll = totalPayroll + pay;
}
System.out.println("Total: $" + totalPayroll);
# Polymorphic list -- one type of loop, three behaviours
staff = []
staff.append(Employee("Alice", 50000.0))    # .0 mirrors Java's double, so output matches
staff.append(Manager("Bob", 60000.0, 10000.0))
staff.append(SalesPerson("Charlie", 40000.0, 0.05, 200000.0))

total_payroll = 0
for i in range(len(staff)):
    pay = staff[i].calculate_pay()
    print(staff[i].get_name() + ": $" + str(pay))
    total_payroll = total_payroll + pay
print("Total: $" + str(total_payroll))

Code ထဲက မှတ်ချက်အရ ဒါက type တစ်ခုတည်းနဲ့ behaviour သုံးမျိုး ရှိတဲ့ polymorphic array ပါ။ Python မှာ .0 ထည့်ရေးထားတာက Java ရဲ့ double နဲ့ ကိုက်ပြီး output တူအောင်ပါ။

Output:

Alice: $50000.0
Bob: $70000.0
Charlie: $50000.0
Total: $170000.0

Loop တစ်ခုတည်းက employee type သုံးမျိုးလုံးကို ကိုင်တွယ်ပါတယ်။ calculatePay() ခေါ်မှု တစ်ခုစီက override လုပ်ထားတဲ့ မှန်ကန်တဲ့ version ဆီ ရောက်သွားပါတယ်။


Quick Check (အမြန် စစ်ဆေးမေးခွန်း)

Q1. Polymorphism က ဘာကို လုပ်ခွင့် ပေးသလဲ။

Q2. Animal a = new Dog("Max", 3, "Lab");: a.getBreed() ကို ခေါ်လို့ ရသလား။

Q3. Animal a = new Dog("Rex", 2, "Poodle");: a.speak() က ဘာကို ပြန်ပေးသလဲ။

Q4. Program တစ်ခုက Animal[] array တစ်ခုကို စီမံပါတယ်။ Bird class အသစ် တစ်ခု ထပ်ထည့်လိုက်ပါတယ်။ Loop ထဲမှာ ဘာတွေ ပြင်ဖို့ လိုသလဲ။


Trace Exercise (code ကို တစ်ဆင့်ချင်း လိုက်ကြည့်ခြင်း)

Polymorphic method ခေါ်မှုတွေကို trace (တစ်ဆင့်ချင်း လိုက်ကြည့်ပြီး မှတ်) လုပ်ပါ။ Employee type တစ်ခုစီမှာ မတူတဲ့ calculatePay() ရှိပါတယ်။ အဖြေတွေကို code ထဲကအတိုင်း English လို ရိုက်ထည့်ပါ (တကယ့် type ကော်လံမှာ class နာမည်ကို ရိုက်ပါ)။

Trace: Polymorphic Dispatch

Employee[] staff = new Employee[3];
staff[0] = new Employee("Alice", 50000);
staff[1] = new Manager("Bob", 60000, 10000);
staff[2] = new SalesPerson("Charlie", 40000, 0.05, 200000);
Indexတကယ့် typecalculatePay()
0
1
2

စုစုပေါင်း လစာ (total payroll):

SalesPerson Charlie: base (အခြေခံ လစာ) 40000 + (0.05 * 200000) = 40000 + 10000 = 50000။


Predict the Output (output ကို ကြိုခန့်မှန်းခြင်း)

ဒီ code က ဘာကို print ထုတ်သလဲ။

Animal[] zoo = new Animal[2];
zoo[0] = new Dog("Max", 3, "Lab");
zoo[1] = new Cat("Luna", 4);
System.out.println(zoo[0].speak());
System.out.println(zoo[1].speak());

Line နှစ်ကြောင်းလုံးကို ကြားမှာ \n ခံပြီး ရိုက်ထည့်ပါ:


Practice Exercises (လေ့ကျင့်ခန်းများ)

Core (အခြေခံ အဆင့်)

  1. Polymorphic array: Dog 2 ကောင်နဲ့ Cat 2 ကောင် ပါတဲ့ Animal[] တစ်ခုကို ဖန်တီးပါ။ getName() နဲ့ speak() ကို သုံးပြီး animal တစ်ကောင်စီရဲ့ နာမည်နဲ့ အသံကို print ထုတ်တဲ့ loop တစ်ခုကို ရေးပါ။

  2. Compile time နဲ့ runtime: Animal a = new Dog("Rex", 5, "Poodle"); ကို ပေးထားပါတယ်။ ဒီထဲက ဘယ်ဟာတွေ compile ဖြစ်ပြီး ဘယ်ဟာတွေ မဖြစ်လဲ State (ဖော်ပြပါ) လုပ်ပါ။ Compile ဖြစ်တဲ့ဟာတွေအတွက် output ကို State လုပ်ပါ:

    • (a) a.speak()
    • (b) a.getName()
    • (c) a.getBreed()
    • (d) a.getAge()

Extension (တိုးချဲ့ အဆင့်)

  1. လစာ စာရင်း စနစ်: ဥပမာ 1 က Employee hierarchy ကို သုံးပြီး employee 5 ယောက် ပါတဲ့ array တစ်ခုကို ဖန်တီးပါ (Employee၊ Manager နဲ့ SalesPerson ရောထားပါ)။ လူတစ်ယောက်စီရဲ့ နာမည်နဲ့ လစာကို print ထုတ်ပြီး နောက်ဆုံးမှာ စုစုပေါင်းကို print ထုတ်တဲ့ printPayroll(Employee[] staff) method ကို ရေးပါ။

  2. ဘာကြောင့်လဲ ရှင်းပြပါ: ကျောင်းသား တစ်ယောက်က polymorphism ကို မသုံးဘဲ animal type တစ်ခုစီအတွက် သီးခြား if-else စစ်ချက်တွေ ရေးပါတယ်။ ဒီနည်းရဲ့ ပြဿနာ နှစ်ခုနဲ့ polymorphism က အဲဒါတွေကို ဘယ်လို ဖြေရှင်းလဲ Explain (ရှင်းပြပါ) လုပ်ပါ။

Challenge (စိန်ခေါ်မှု အဆင့်)

  1. ဖုန်း plan တွေကို နှိုင်းယှဉ်ခြင်း: getMonthlyCost() နဲ့ getDataGB() ပါတဲ့ PhonePlan parent တစ်ခုကို ဖန်တီးပါ။ Method နှစ်ခုလုံးကို override လုပ်တဲ့ child သုံးခု ဖန်တီးပါ: PayAsYouGo (သုံးတဲ့ GB တစ်ခုစီအတွက် 3.0 ကောက်တယ်)၊ FixedPlan (သတ်မှတ်ထားတဲ့ ဒေတာ ပမာဏအတွက် လစဉ် ကြေး ပုံသေ) နဲ့ SharedPlan (အခြေခံ ကြေး 12.0 နဲ့ လူတစ်ယောက်ကို 6.0 ပေါင်း၊ လူတစ်ယောက်ကို ဒေတာ 10 GB)။ ဒါတွေကို ရောပြီး PhonePlan[] array ထဲ သိမ်းပါ၊ ပြီးရင် GB တစ်ခုစီရဲ့ ကုန်ကျစရိတ် (လစဉ် ကုန်ကျစရိတ်ကို ဒေတာနဲ့ စား) အနည်းဆုံး plan ကို return (ရလဒ်အဖြစ် ပြန်ပေး) လုပ်တဲ့ method တစ်ခု ရေးပါ။ Array ထဲက plan တိုင်းမှာ ဒေတာ 0 GB ထက် ပိုရှိပြီး array ထဲမှာ plan အနည်းဆုံး တစ်ခု ရှိပါတယ်။

ဆက်စပ် ခေါင်းစဉ်များ

  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: Inheritance (parent class ကနေ အမွေဆက်ခံခြင်း): polymorphism အတွက် inheritance လိုပါတယ်
  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: Method Overriding (parent ရဲ့ method ကို child က အစားထိုး ရေးခြင်း): polymorphism က ရွေးပြီး run ပေးတာ override လုပ်ထားတဲ့ method တွေပါ
  • နောက်ထပ် လေ့လာမယ့် ခေါင်းစဉ်: Abstract Classes (object တိုက်ရိုက် မဖန်တီးနိုင်တဲ့ class များ): subclass တွေကို method တွေ မဖြစ်မနေ override လုပ်ခိုင်းခြင်း
  • ဆက်စပ် ခေါင်းစဉ်: Aggregation (သီးခြား ရှိနေနိုင်တဲ့ has-a ဆက်နွယ်မှု): polymorphic array တွေက aggregation နဲ့ inheritance ကို ပေါင်းသုံးထားတာပါ

© EduCS.me. ကွန်ပျူတာသိပ္ပံ ပညာရေးအတွက် အရင်းအမြစ်တွေ စုထားတဲ့ နေရာပါ။ EduCS is an independent educational resource. It is not affiliated with, or endorsed by, the International Baccalaureate Organization. (EduCS ဟာ သီးခြား ပညာရေး အရင်းအမြစ်တစ်ခုပါ။ International Baccalaureate Organization နဲ့ ဆက်စပ်မှု မရှိသလို သူတို့ရဲ့ ထောက်ခံချက်လည်း မရှိပါဘူး။) ဆက်သွယ်ရန်