Abstract Classes (object တိုက်ရိုက် မဖန်တီးနိုင်တဲ့ class များ)

B3.2 (HL): Use abstract classes to define incomplete base types that subclasses must complete.
မြန်မာလို: subclass (အမွေဆက်ခံတဲ့ child class) တွေက ဆက်ဖြည့်ပြီး ပြီးပြည့်စုံအောင် လုပ်ရမယ့် မပြည့်စုံသေးတဲ့ base type (အခြေခံ အမျိုးအစား) တွေကို သတ်မှတ်ဖို့ abstract class (object တိုက်ရိုက် မဖန်တီးနိုင်တဲ့ class) တွေကို သုံးနိုင်ရပါမယ်။

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


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

Concrete Parent Class တွေရဲ့ ပြဿနာ

Inheritance (parent class ကနေ အမွေဆက်ခံခြင်း) နဲ့ Polymorphism (ပုံစံ အမျိုးမျိုး ယူနိုင်ခြင်း) စာမျက်နှာတွေမှာ Animal class (object တွေ ဖန်တီးဖို့ ပုံစံခွက်) ထဲမှာ "..." ကို return (ရလဒ်အဖြစ် ပြန်ပေး) လုပ်တဲ့ speak() method (သီးခြား အလုပ်တစ်ခု လုပ်ပေးတဲ့ code အပိုင်း) တစ်ခု ပါပါတယ်:

public class Animal {
    private String name;
    private int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    public String speak() {
        return "...";  // meaningless default
    }
}
class Animal():
    def __init__(self, name, age):
        self._name = name
        self._age = age

    def get_name(self):
        return self._name

    def get_age(self):
        return self._age

    def speak(self):
        return "..."    # meaningless default

ဒါက အလုပ်လုပ်ပါတယ်၊ ဒါပေမယ့် ပြဿနာ နှစ်ခု ရှိပါတယ်:

  1. တစ်ယောက်ယောက်က new Animal("Generic", 1) လို့ ရေးတာကို ဘာကမှ မတားပါဘူး: “…” လို့ ပြောတဲ့ ယေဘုယျ တိရစ္ဆာန် တစ်ကောင်က အဓိပ္ပာယ် မရှိပါဘူး။ “…” လို့ပဲ ပြောတဲ့ တကယ့် တိရစ္ဆာန် မရှိပါဘူး။
  2. Subclass က speak() ကို override (parent ရဲ့ method ကို child က အစားထိုး ရေးခြင်း) လုပ်ဖို့ မေ့သွားနိုင်ပါတယ်၊ ဒီလိုဆိုရင် အသုံးမဝင်တဲ့ default (သတ်မှတ်မထားရင် သုံးတဲ့) version ကို ဘာ သတိပေးချက်မှ မရှိဘဲ အမွေဆက်ခံသွားပါတယ်။ Program က compile ဖြစ်ပြီး run ပါတယ်၊ ဒါပေမယ့် output (program က ထုတ်ပြတဲ့ ရလဒ်) မှားထွက်ပါတယ်။

Abstract class က ပြဿနာ နှစ်ခုလုံးကို တစ်ပြိုင်နက် ဖြေရှင်းပေးပါတယ်: parent class ကို တိုက်ရိုက် instantiation (class ကနေ object အသစ် ဖန်တီးခြင်း) လုပ်တာကို တားပေးပါတယ်၊ ပြီးတော့ subclass တိုင်းကို အဓိက method တွေရဲ့ ကိုယ်ပိုင် version ကို မဖြစ်မနေ ရေးခိုင်းပါတယ်။

Abstract Class ဆိုတာ ဘာလဲ

Abstract class ဆိုတာ abstract keyword (programming language မှာ အထူး အဓိပ္ပာယ်ရှိတဲ့ စကားလုံး) နဲ့ ကြေညာထားတဲ့ class ပါ။ သူ့ကို new နဲ့ တိုက်ရိုက် instantiate လုပ်လို့ မရပါဘူး။ Subclass တွေက extend (အမွေဆက်ခံ) လုပ်ဖို့အတွက်ပဲ ရှိတာပါ။

public abstract class Animal {
    private String name;
    private int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    // Abstract method -- no body, just a declaration
    public abstract String speak();
}
# Python has no abstract keyword - raising an error if a child forgets
# to override is the closest simple equivalent
class Animal():
    def __init__(self, name, age):
        self._name = name
        self._age = age

    def get_name(self):
        return self._name

    def get_age(self):
        return self._age

    def speak(self):
        raise NotImplementedError    # "abstract": every child MUST override this

အခု Animal က abstract ဖြစ်သွားပါပြီ။ ပြောင်းလဲမှု နှစ်ခု ရှိပါတယ်:

Animal a = new Animal("Generic", 1);  // COMPILE ERROR -- cannot instantiate
a = Animal("Generic", 1)    # Python ALLOWS this - there is no abstract keyword to stop it
a.speak()                   # but the mistake surfaces here: NotImplementedError at runtime
Dog d = new Dog("Max", 3, "Labrador");  // OK -- Dog is concrete
Animal a = d;                            // OK -- polymorphism still works
d = Dog("Max", 3, "Labrador")    # OK -- Dog provides a real speak()
a = d                            # OK -- polymorphism still works

Abstract class ကို variable (တန်ဖိုး သိမ်းထားတဲ့ နာမည်ပေးထားတဲ့ နေရာ)၊ array (အရွယ်အစား ပုံသေ၊ အမျိုးအစားတူ ဒေတာစု) နဲ့ parameter (method ကို ပေးလိုက်တဲ့ တန်ဖိုးကို လက်ခံတဲ့ variable) တွေအတွက် type (အမျိုးအစား) အဖြစ် ဆက်သုံးလို့ ရပါသေးတယ်။ သူ့ရဲ့ instance (class တစ်ခုကနေ ဖန်တီးထားတဲ့ object) ကို တိုက်ရိုက် ဖန်တီးလို့ပဲ မရတာပါ။

Abstract Methods (body မပါဘဲ ကြေညာချက်ပဲ ပါတဲ့ method များ)

Abstract method (body မပါဘဲ ကြေညာချက်ပဲ ပါတဲ့ method) မှာ body (method ထဲမှာ ရေးထားတဲ့ code) မပါပါဘူး။ Signature (method ရဲ့ နာမည်နဲ့ parameter type တွေ) နဲ့ သူ့နောက်က semicolon (;) ပဲ ပါပါတယ်:

public abstract String speak();   // abstract -- no { }
def speak(self):
    raise NotImplementedError    # the only "body" is an error - forces children to override

Animal ကို extend လုပ်တဲ့ class တိုင်းက speak() ကို မဖြစ်မနေ override လုပ်ပြီး body ထည့်ပေးရပါမယ်။ မထည့်ရင် compiler (code တစ်ခုလုံးကို ကြိုတင် ဘာသာပြန်တဲ့ program) က error ပြပါတယ်။ Abstract class တွေက subclass တွေကို အဓိပ္ပာယ်ရှိတဲ့ လုပ်ဆောင်ပုံ ပေးဖို့ ဒီလို မဖြစ်မနေ လုပ်ခိုင်းတာပါ။

public class Dog extends Animal {
    private String breed;

    public Dog(String name, int age, String breed) {
        super(name, age);
        this.breed = breed;
    }

    public String getBreed() { return breed; }

    @Override
    public String speak() {
        return "Woof!";  // required -- Dog MUST implement speak()
    }
}
class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)    # call the parent constructor
        self._breed = breed

    def get_breed(self):
        return self._breed

    def speak(self):
        return "Woof!"    # overriding replaces the error-raising version

Dog ထဲက speak() method ကို ဖယ်လိုက်ရင် ဒီ error ကို ရပါမယ်:

Dog is not abstract and does not override abstract method speak() in Animal

ဒီ error message က “Dog က abstract class မဟုတ်ပါဘူး၊ ပြီးတော့ Animal ထဲက abstract method speak() ကို override မလုပ်ထားပါဘူး” လို့ ဆိုလိုပါတယ်။

(Java က ဒီအမှားကို compile time (compile လုပ်ချိန်) မှာ ဖမ်းမိပါတယ်။ Python မှာတော့ ဒီအမှား တူတူက runtime (program run နေစဉ်) ရောက်မှပဲ ပေါ်လာပါတယ်: speak() ကို ပထမဆုံး ခေါ်တဲ့အခါ NotImplementedError အဖြစ် ပေါ်လာတာပါ။)

Abstract Class တွေရဲ့ စည်းမျဉ်းများ

စည်းမျဉ်း ဥပမာ
abstract keyword နဲ့ ကြေညာပါတယ် public abstract class Animal { }
new နဲ့ instantiate လုပ်လို့ မရပါဘူး new Animal(...) က compile error (compile လုပ်ချိန်မှာ ဖြစ်တဲ့ error) ပါ
Constructor (object ဖန်တီးချိန် attribute တွေကို စတင် သတ်မှတ်ပေးတဲ့ method) တွေ ပါလို့ ရပါတယ် Subclass တွေက super(...) နဲ့ ခေါ်ပါတယ်
Concrete (abstract မဟုတ်တဲ့ ပုံမှန်) method တွေ ပါလို့ ရပါတယ် getName(), getAge() က ပုံမှန်အတိုင်း အလုပ်လုပ်ပါတယ်
Abstract method တွေ ပါလို့ ရပါတယ် public abstract String speak();
Class ထဲမှာ abstract method တစ်ခုခု ပါရင် class ကို abstract လို့ ကြေညာရပါမယ် Concrete class ထဲမှာ abstract method ထားလို့ မရပါဘူး
Concrete subclass က abstract method အားလုံးကို implement (တကယ် အလုပ်လုပ်အောင် ရေး) လုပ်ရပါမယ် မလုပ်ရင် သူ့ကိုလည်း abstract လို့ ကြေညာရပါမယ်
Type အဖြစ် သုံးလို့ ရပါတယ် Animal[] zoo = new Animal[5]; က မှန်ကန်ပါတယ်

Abstract class တွေမှာ concrete method နဲ့ abstract method ကို ရောထားလို့ ရပါတယ်။ Concrete method တွေက subclass အားလုံး အမွေရမယ့် မျှသုံး လုပ်ဆောင်ပုံကို ပေးပါတယ်။ Abstract method တွေကတော့ subclass တစ်ခုစီ လိုက်နာရမယ့် “contract” (လိုက်နာရမယ့် သဘောတူချက်) ကို သတ်မှတ်ပါတယ်။

Abstract Class နဲ့ Polymorphism

Abstract class တွေက polymorphism နဲ့ ကောင်းကောင်း တွဲပြီး အလုပ်လုပ်ပါတယ်။ Polymorphic array (မတူတဲ့ subclass တွေရဲ့ object တွေကို အတူ သိမ်းထားတဲ့ array) နဲ့ loop (ထပ်ခါထပ်ခါ run တဲ့ code အပိုင်း) တွေကို ဆက်ရေးလို့ ရပါသေးတယ်:

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

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"))

for i in range(len(zoo)):
    print(zoo[i].get_name(), "says", zoo[i].speak())

အရင်နဲ့ ကွာတာက: new Animal(...) ကို array ထဲ မတော်တဆ ထည့်မိတာ ဘယ်တော့မှ မဖြစ်နိုင်တော့ပါဘူး။ Element (အစုထဲက အချက်) တိုင်းက တကယ့် speak() implementation (တကယ် အလုပ်လုပ်အောင် ရေးထားတဲ့ code) ပါတဲ့ concrete subclass ဖြစ်ရပါမယ်။ ဒါက code ကို ပိုလုံခြုံစေပါတယ်။

Racer ဥပမာကို ပြန်ကြည့်ခြင်း

Inheritance စာမျက်နှာက Racer hierarchy (parent နဲ့ child class တွေကို အဆင့်ဆင့် ဖွဲ့ထားတဲ့ ပုံစံ) ကို ပြန်သတိရကြည့်ပါ။ Racer class မှာ 1 လှမ်း ရှေ့တိုးတဲ့ default move() ပါပါတယ်။ ဒါပေမယ့် ယေဘုယျ Racer object တစ်ခုက ပြိုင်ပွဲမှာ အဓိပ္ပာယ် မရှိပါဘူး: Hare (ယုန်) နဲ့ Tortoise (လိပ်) ကပဲ အဓိပ္ပာယ် ရှိပါတယ်။

Racer နဲ့ move() ကို abstract လုပ်လိုက်ရင် racer အမျိုးအစား တိုင်းက ကိုယ့်ရဲ့ ရွေ့ပုံကို ကိုယ်တိုင် သတ်မှတ်ရပါတယ်:

public abstract class Racer {
    private String name;
    private int position;

    public Racer(String name) {
        this.name = name;
        this.position = 0;
    }

    public String getName() { return name; }
    public int getPosition() { return position; }

    // Concrete helper -- subclasses can call this to advance
    protected void advance(int steps) {
        position = position + steps;
    }

    // Abstract -- each racer type defines HOW it moves
    public abstract void move();
}
class Racer():
    def __init__(self, name):
        self._name = name
        self._position = 0

    def get_name(self):
        return self._name

    def get_position(self):
        return self._position

    # Concrete helper -- subclasses call this to advance
    def advance(self, steps):
        self._position = self._position + steps

    # "Abstract" -- each racer type defines HOW it moves
    def move(self):
        raise NotImplementedError    # Python's closest simple stand-in for abstract
public class Hare extends Racer {
    public Hare(String name) {
        super(name);
    }

    @Override
    public void move() {
        if (Math.random() < 0.3) {
            advance(5);  // big jump
        }
        // else: sleep (do nothing)
    }
}

public class Tortoise extends Racer {
    public Tortoise(String name) {
        super(name);
    }

    @Override
    public void move() {
        advance(1);  // slow and steady
    }
}
import random

class Hare(Racer):
    def __init__(self, name):
        super().__init__(name)    # call the parent constructor

    def move(self):
        if random.random() < 0.3:
            self.advance(5)    # big jump
        # else: sleep (do nothing)

class Tortoise(Racer):
    def __init__(self, name):
        super().__init__(name)

    def move(self):
        self.advance(1)    # slow and steady

advance() helper method (တခြား method တွေက ခေါ်သုံးဖို့ ရေးထားတဲ့ အကူ method) က protected (subclass တွေကလည်း သုံးခွင့်ရတဲ့ access modifier) ဖြစ်ပြီး concrete ဖြစ်တာကို သတိထားကြည့်ပါ: racer အားလုံးက position ကို ပြောင်းတဲ့ လုပ်ပုံ အတူတူကို မျှသုံးပါတယ်။ ရွေ့ပုံ (ဘယ်လောက် ဝေးဝေး၊ ဘယ်အချိန်မှာ ရွေ့မလဲ) ကပဲ abstract ဖြစ်ပါတယ်။ ဒါက အသုံးများတဲ့ ပုံစံ တစ်ခုပါ: abstract class တွေက မျှသုံးရမယ့် အခြေခံ အစိတ်အပိုင်းတွေကို ပေးထားပြီး သီးခြား ဆုံးဖြတ်ချက်တွေကိုတော့ subclass တွေဆီ ချန်ထားပါတယ်။


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

ဥပမာ 1: Shape Hierarchy

ပုံဆွဲတဲ့ application တစ်ခုမှာ shape (ပုံသဏ္ဌာန်) အမျိုးမျိုး လိုပါတယ်၊ တစ်ခုစီမှာ ကိုယ်ပိုင် ဧရိယာ တွက်နည်း ရှိပါတယ်။

public abstract class Shape {
    private String colour;

    public Shape(String colour) {
        this.colour = colour;
    }

    public String getColour() { return colour; }

    // Every shape must calculate its own area
    public abstract double getArea();

    // Concrete method shared by all shapes
    public String describe() {
        return colour + " shape with area " + getArea();
    }
}

public class Circle extends Shape {
    private double radius;

    public Circle(String colour, double radius) {
        super(colour);
        this.radius = radius;
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public class Rectangle extends Shape {
    private double width;
    private double height;

    public Rectangle(String colour, double width, double height) {
        super(colour);
        this.width = width;
        this.height = height;
    }

    @Override
    public double getArea() {
        return width * height;
    }
}
import math

class Shape():
    def __init__(self, colour):
        self._colour = colour

    def get_colour(self):
        return self._colour

    # Every shape must calculate its own area
    def get_area(self):
        raise NotImplementedError    # "abstract" - Python has no abstract keyword

    # Concrete method shared by all shapes
    def describe(self):
        return self._colour + " shape with area " + str(self.get_area())

class Circle(Shape):
    def __init__(self, colour, radius):
        super().__init__(colour)    # call the parent constructor
        self._radius = radius

    def get_area(self):
        return math.pi * self._radius * self._radius

class Rectangle(Shape):
    def __init__(self, colour, width, height):
        super().__init__(colour)
        self._width = width
        self._height = height

    def get_area(self):
        return self._width * self._height
Shape[] shapes = new Shape[3];
shapes[0] = new Circle("Red", 5);
shapes[1] = new Rectangle("Blue", 4, 6);
shapes[2] = new Circle("Green", 3);

double totalArea = 0;
for (int i = 0; i < shapes.length; i++) {
    System.out.println(shapes[i].describe());
    totalArea = totalArea + shapes[i].getArea();
}
System.out.println("Total area: " + totalArea);
shapes = []
shapes.append(Circle("Red", 5))
shapes.append(Rectangle("Blue", 4.0, 6.0))    # .0 mirrors Java's double, so 24.0 prints
shapes.append(Circle("Green", 3))

total_area = 0
for i in range(len(shapes)):
    print(shapes[i].describe())
    total_area = total_area + shapes[i].get_area()
print("Total area:", total_area)

Output:

Red shape with area 78.53981633974483
Blue shape with area 24.0
Green shape with area 28.274333882308138
Total area: 130.81415022205297

သတိပြုစရာ အဓိက အချက်များ:

  • Shape က abstract ပါ: new Shape("Red") လို့ ရေးလို့ မရပါဘူး
  • describe() က concrete ဖြစ်ပြီး abstract getArea() ကို ခေါ်ပါတယ်: runtime မှာ မှန်ကန်တဲ့ version က run ပါတယ်
  • Polymorphic array နဲ့ loop က အရင်ကအတိုင်း အတိအကျ အလုပ်လုပ်ပါတယ်

ဥပမာ 2: Game ဇာတ်ကောင်တွေရဲ့ လုပ်ဆောင်ချက်များ

Game တစ်ခုမှာ ဇာတ်ကောင် (character) အမျိုးအစား အမျိုးမျိုး ရှိပြီး တစ်ခုစီမှာ ကိုယ်ပိုင် တိုက်ခိုက်ပုံ ရှိပါတယ်။

public abstract class GameCharacter {
    private String name;
    private int health;

    public GameCharacter(String name, int health) {
        this.name = name;
        this.health = health;
    }

    public String getName() { return name; }
    public int getHealth() { return health; }

    public void takeDamage(int amount) {
        health = health - amount;
        if (health < 0) {
            health = 0;
        }
    }

    public boolean isAlive() {
        return health > 0;
    }

    // Each character type attacks differently
    public abstract int attack();
    public abstract String getAttackName();
}

public class Warrior extends GameCharacter {
    public Warrior(String name) {
        super(name, 100);
    }

    @Override
    public int attack() {
        return 15;  // heavy consistent damage
    }

    @Override
    public String getAttackName() {
        return "Sword Strike";
    }
}

public class Mage extends GameCharacter {
    public Mage(String name) {
        super(name, 70);
    }

    @Override
    public int attack() {
        // 40% chance of critical spell (30 damage), otherwise 10
        if (Math.random() < 0.4) {
            return 30;
        }
        return 10;
    }

    @Override
    public String getAttackName() {
        return "Fireball";
    }
}
import random

class GameCharacter():
    def __init__(self, name, health):
        self._name = name
        self._health = health

    def get_name(self):
        return self._name

    def get_health(self):
        return self._health

    def take_damage(self, amount):
        self._health = self._health - amount
        if self._health < 0:
            self._health = 0

    def is_alive(self):
        return self._health > 0

    # Each character type attacks differently ("abstract" - children must override)
    def attack(self):
        raise NotImplementedError

    def get_attack_name(self):
        raise NotImplementedError

class Warrior(GameCharacter):
    def __init__(self, name):
        super().__init__(name, 100)    # call the parent constructor

    def attack(self):
        return 15    # heavy consistent damage

    def get_attack_name(self):
        return "Sword Strike"

class Mage(GameCharacter):
    def __init__(self, name):
        super().__init__(name, 70)

    def attack(self):
        # 40% chance of critical spell (30 damage), otherwise 10
        if random.random() < 0.4:
            return 30
        return 10

    def get_attack_name(self):
        return "Fireball"
GameCharacter[] team = new GameCharacter[2];
team[0] = new Warrior("Aria");
team[1] = new Mage("Zeph");

for (int i = 0; i < team.length; i++) {
    int damage = team[i].attack();
    System.out.println(team[i].getName() + " uses " +
        team[i].getAttackName() + " for " + damage + " damage");
}
team = []
team.append(Warrior("Aria"))
team.append(Mage("Zeph"))

for i in range(len(team)):
    damage = team[i].attack()
    print(team[i].get_name(), "uses", team[i].get_attack_name(), "for", damage, "damage")

Output (run ကြည့်တဲ့ ဥပမာ တစ်ခု):

Aria uses Sword Strike for 15 damage
Zeph uses Fireball for 30 damage

GameCharacter မှာ abstract method နှစ်ခု (attack() နဲ့ getAttackName()) ပါတာကို သတိထားကြည့်ပါ။ Warrior ရော Mage ပါ နှစ်ခုလုံးကို implement လုပ်ရပါမယ်။ Concrete method တွေ ဖြစ်တဲ့ takeDamage(), isAlive(), getName() နဲ့ getHealth() ကိုတော့ ဇာတ်ကောင် အားလုံးက မျှသုံးပါတယ်။


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

Q1. Animal က abstract ဖြစ်နေရင် new Animal("Rex", 3) လို့ ရေးလိုက်တဲ့အခါ ဘာဖြစ်မလဲ။

Q2. Class တစ်ခုမှာ abstract method တစ်ခုနဲ့ concrete method သုံးခု ပါပါတယ်။ ဒီ class က ဘာ ဖြစ်ရမလဲ:

Q3. Shape က abstract ဖြစ်ပြီး getArea() နဲ့ getPerimeter() ဆိုတဲ့ abstract method နှစ်ခု ပါပါတယ်။ Circle က Shape ကို extend လုပ်ပြီး getArea() ကိုပဲ implement လုပ်ထားပါတယ်။ ဘာဖြစ်မလဲ။

Q4. Abstract class မှာ constructor ပါလို့ ရသလား။

Q5. Animal က abstract ဖြစ်ပါတယ်။ Animal[] zoo = new Animal[5]; က မှန်ကန်တဲ့ code လား။


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

Shape hierarchy ထဲက method call (method ကို ခေါ်သုံးခြင်း) တွေကို trace (တစ်ဆင့်ချင်း လိုက်ပြီး မှတ်) လုပ်ပါ။ Shape က abstract ဖြစ်ပြီး abstract getArea() ပါပါတယ်။ Type နာမည်တွေကို code ထဲကအတိုင်း English လို ရိုက်ထည့်ပါ။

Trace: Abstract class နဲ့ polymorphism

// Circle: getArea() returns Math.PI * r * r
// Rectangle: getArea() returns w * h
Shape[] shapes = new Shape[3];
shapes[0] = new Circle("Red", 5);
shapes[1] = new Rectangle("Blue", 4, 6);
shapes[2] = new Circle("Green", 3);
Index (နေရာအမှတ်)တကယ့် typegetArea() (အနီးစပ်ဆုံး)
0
1
2

စုစုပေါင်း ဧရိယာ (အနီးစပ်ဆုံး):

Circle ရဲ့ ဧရိယာ: PI * r^2။ r=5 အတွက်: 3.14159 * 25 = 78.54။ r=3 အတွက်: 3.14159 * 9 = 28.27။


Spot the Error (အမှား ရှာခြင်း)

ဒီ code မှာ bug (code ထဲက အမှား) တစ်ခု ပါပါတယ်။ Vehicle class ကို abstract ဖြစ်စေချင်တာပါ၊ ဒါပေမယ့် programmer က အမှား တစ်ခု လုပ်မိထားပါတယ်။

အမှား ရှာပါ: ယာဉ် အမျိုးအစား တိုင်းက ကိုယ့်ရဲ့ ဆီစားနှုန်း (fuel efficiency) ကို ပြန်ပေးရမယ့် vehicle hierarchy တစ်ခုပါ။ အမှားကို ရှာပြီး ပြင်ပါ။

1public class Vehicle { 2 private String model; 3 4 public Vehicle(String model) { 5 this.model = model; 6 } 7 8 public String getModel() { return model; } 9 10 public abstract double getFuelEfficiency(); 11}

Line 1 အတွက် မှန်ကန်တဲ့ ပြင်နည်းကို ရွေးပါ:


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

Abstract Shape class မှာ colour + " shape with area " + getArea() ကို return ပြန်ပေးတဲ့ describe() ပါပါတယ်။ ဒီ code က ဘာကို print ထုတ်မလဲ။

Shape s1 = new Circle("Red", 4);
Shape s2 = new Rectangle("Blue", 3, 7);
System.out.println(s2.describe());

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

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

  1. Employee ကို abstract လုပ်ပါ: Polymorphism စာမျက်နှာရဲ့ ဥပမာ 1 ထဲက Employee, Manager နဲ့ SalesPerson class တွေကို ယူပါ။ Employee ကို abstract လုပ်ပြီး String (စာသား) တစ်ခုကို return ပြန်ပေးတဲ့ abstract method getRole() ကို ထည့်ပါ: Manager က “Manager” ကို၊ SalesPerson က “Sales” ကို return ပြန်ပေးပါတယ်။ calculatePay() ကိုတော့ body ပါတဲ့ ပုံမှန် method အဖြစ်ပဲ ထားပါ။ အဲဒီ ဥပမာရဲ့ test code ထဲက ဘယ် line က compile မဖြစ်တော့ဘူးလဲ၊ ဘာကြောင့်လဲ။ Bob နဲ့ Charlie ရဲ့ နာမည်၊ role (ရာထူး) နဲ့ pay (လစာ) ကို print ထုတ်အောင် test ကို ပြင်ပါ။

  2. တူရိယာ hierarchy: name နဲ့ type (ဥပမာ “string” (ကြိုးတူရိယာ), “percussion” (တီးခတ်တူရိယာ)) ဆိုတဲ့ attribute တွေနဲ့ String ကို return ပြန်ပေးတဲ့ abstract method play() ပါတဲ့ abstract Instrument class တစ်ခု ဖန်တီးပါ။ Guitar (“Strum!” ကို return ပြန်ပေးတယ်) နဲ့ Drum (“Boom!” ကို return ပြန်ပေးတယ်) ဆိုတဲ့ concrete subclass တွေကို ဖန်တီးပါ။ သူတို့ကို Instrument[] array ထဲမှာ သိမ်းပြီး တူရိယာ တစ်ခုစီရဲ့ နာမည်နဲ့ အသံကို print ထုတ်ပါ။

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

  1. ငွေပေးချေမှု စနစ်: boolean (true ဒါမှမဟုတ် false တန်ဖိုးပဲ ယူတဲ့) တန်ဖိုးကို return ပြန်ပေးတဲ့ abstract method processPayment(double amount) ပါတဲ့ abstract PaymentMethod class တစ်ခု ဖန်တီးပါ။ CreditCard (အမြဲ အောင်မြင်တယ်) နဲ့ CashPayment (amount > balance ဖြစ်ရင် မအောင်မြင်ဘူး) ဆိုတဲ့ concrete subclass တွေကို ဖန်တီးပါ။ Payment method တွေရဲ့ polymorphic array တစ်ခုနဲ့ စမ်းသပ်ပါ။

  2. Abstract class အဆင့်ဆင့်: abstract method getValue() ပါတဲ့ abstract class A ကို ဖန်တီးပါ။ abstract method အသစ် getLabel() ကို ထပ်ထည့်တဲ့ abstract class B extends A ကို ဖန်တီးပါ။ နှစ်ခုလုံးကို implement လုပ်တဲ့ concrete class C extends B ကို ဖန်တီးပါ။ B က getValue() ကို ဘာကြောင့် implement လုပ်ဖို့ မလိုလဲဆိုတာ Explain (ရှင်းပြပါ) လုပ်ပါ။

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

  1. ပြိုင်ပွဲ စနစ်: Abstract Racer class ကို extend လုပ်ပြီး racer အမျိုးအစား သုံးမျိုးကို ပံ့ပိုးပါ: Hare (30% ဖြစ်နိုင်ခြေနဲ့ 5 လှမ်း ခုန်တယ်)၊ Tortoise (အမြဲ 1 လှမ်း) နဲ့ အသစ် Cheetah (အမြဲ 3 လှမ်း၊ ဒါပေမယ့် position 80 ကို ကျော်ပြီးရင် လုံးဝ ရပ်သွားတယ်)။ အမျိုးအစား တစ်ခုစီက တစ်ကောင်စီနဲ့ round 100 ပြိုင်ပွဲ တစ်ခုကို run ပြီး နောက်ဆုံး အဆင့်တွေကို position အလိုက် စီပြီး print ထုတ်ပါ။

Exam-Style (စာမေးပွဲ ပုံစံ မေးခွန်း၊ 11 marks)

[1 mark]၊ [3 marks] စတာတွေက အပိုင်း တစ်ခုချင်းစီအတွက် ရနိုင်တဲ့ အမှတ်ပါ။ IB စာမေးပွဲ မေးခွန်းတွေမှာ အမှတ်ကို ဒီလို ပြလေ့ ရှိပါတယ်။

6. Report က abstract class တစ်ခုပါ။ Report r = new Report("Sales"); line က ဘာကြောင့် compile မဖြစ်လဲဆိုတာ State (ဖော်ပြပါ) လုပ်ပါ။ [1 mark]

7. Smart-home app (အိမ်က စက်ပစ္စည်းတွေကို ထိန်းချုပ်တဲ့ app) တစ်ခုက ဒီ abstract class ကို သုံးပါတယ်:

public abstract class Device {
    protected String location;

    public Device(String location) {
        this.location = location;
    }

    public abstract void activate();
}
class Device():
    def __init__(self, location):
        self.location = location    # no underscore = reachable from subclasses, like Java's protected

    def activate(self):
        raise NotImplementedError    # "abstract": every child MUST override this

(a) Device d = new Device("Kitchen"); line က ဘာကြောင့် compile error ဖြစ်စေလဲဆိုတာ Explain လုပ်ပါ။ [2 marks]

(b) Device ကို extend လုပ်ပြီး location + " light on" ကို print ထုတ်အောင် activate() ကို implement လုပ်တဲ့ concrete class SmartLight ကို Construct (ကိုယ်တိုင် တည်ဆောက်ပါ) လုပ်ပါ။ [3 marks]

(c) SmartHome object တစ်ခုက ArrayList<Device> (ကြီးလာ၊ သေးသွားနိုင်တဲ့ list) တစ်ခုကို ကိုင်ထားပြီး တခြားနေရာမှာ ဖန်တီးထားပြီး ပေးပို့လာတဲ့ device တွေကို အဲဒီထဲ ထည့်ပါတယ်။ ဒါက composition (ပိုခိုင်မာတဲ့ has-a ဆက်နွယ်မှု) လား aggregation (သီးခြား ရှိနေနိုင်တဲ့ has-a ဆက်နွယ်မှု) လားဆိုတာ State လုပ်ပြီး ကိုယ့်အဖြေကို justify (ဘာကြောင့် ရွေးတာလဲ အကြောင်းပြချက်နဲ့ ပြပါ) လုပ်ပါ။ [3 marks]

(d) ဒီ app မှာ abstract Device class ကို သုံးတဲ့ အကျိုးကျေးဇူး တစ်ခုကို Explain လုပ်ပါ။ [2 marks]

အပိုင်း (c) က composition နဲ့ aggregation ကွာခြားချက်ကို သုံးပါတယ်၊ အဲဒါကို Composition vs Inheritance စာမျက်နှာမှာ ရှင်းပြထားပါတယ်။


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

  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: Inheritance: abstract class တွေက extends/super ပုံစံပေါ်မှာ အခြေခံပါတယ်
  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: Method Overriding: abstract method တွေက override လုပ်ဖို့ မဖြစ်မနေ လိုအပ်စေပါတယ်
  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: Polymorphism: abstract type တွေက polymorphic variable type တွေအဖြစ် အလုပ်လုပ်ပါတယ်
  • နောက်ထပ် လေ့လာမယ့် ခေါင်းစဉ်: Interfaces: state (attribute တွေမှာ လက်ရှိ ရှိနေတဲ့ တန်ဖိုးများ) မပါတဲ့ contract သက်သက်တွေ၊ class တစ်ခုက interface အများကို implement လုပ်နိုင်ခြင်း
  • ဆက်စပ် ခေါင်းစဉ်: What is OOP? (OOP ဆိုတာ ဘာလဲ): abstraction (မလိုတဲ့ အသေးစိတ်ကို ဖယ်ပြီး အဓိကကိုပဲ ကြည့်ခြင်း) က OOP (object တွေကို အခြေခံပြီး program ရေးနည်း) ရဲ့ အခြေခံ principle (မူ) လေးခုထဲက တစ်ခုပါ

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