php

Inheritance, Trait and Delegation in PHP

ကျွန်တော်တို့ PHP ရေးတဲ့အခါ Inheritance, Trait နဲ့ Delegation ကို သုံးခုတွဲပြီး နားလည်အောင် လုပ်ထားမယ်ဆိုရင် Development သွားတဲ့အခါ အများကြီး အထောက်အကူပြုပါတယ်။ ဒါပေမယ့် ဒီအကြောင်းအရာတွေ အားလုံးဟာ လက်တွေ့လုပ်ငန်းခွင်နဲ့ ချိတ်ဆက်မိမှသာ အလုပ်ဖြစ် အသက်ဝင်နိုင်ပါတယ်။

Inheritance

Inheritance ဆိုတာကတော့ အားလုံးသိပြီး ဖြစ်မှာပါ။ Parent Object တစ်ခုလုံးရဲ့ Power ကို Child Object က ယူလိုက်တာပါပဲ။ ကျွန်တော်တို့ နမူနာလေးတွေ ရေးကြည့်ရအောင်

<?php
interface AnimalInterface {
    public function eat();
}
interface DogInterface {
    public function bark();
}
abstract class Animal implements AnimalInterface {
    public $name; public function eat() {
        echo "EAT! <br>";
    }

    public function __construct($name) {
        $this->name = $name;
        echo "Animal Contruct! <br>";
    }
}
// Inheritance
class Dog extends Animal implements DogInterface {
    public $foo;
    public function __construct($name, $foo)  {
        parent::__construct($name);
        $this->foo = $foo;
    }
    public function bark() {
        echo "Bark! <br>";
    }
}
$dog = new Dog("Aung Net", "bar");
$dog->type  = "Test";
$dog->bark();
$dog->eat();
?>

အပေါ်မှာ ရေးထားတာကတော့ Inheritance ပုံစံ ဖြစ်ပါတယ်။ Parent Class မှာ ပါတဲ့ eat() ဆိုတဲ့ Method ကို Dog Class က Inheritance လုပ်လိုက်တာနဲ့ သုံးလို့သွားပါတယ်။ ဒါကတော့ အားလုံးသိပြီး ဖြစ်မယ် ထင်ပါတယ်။

Trait

နောက်တစ်ခုကတော့ Trait ပါ။ Trait ဆိုတာ Pattern တစ်ခု ဖြစ်ပြီး PHP ရဲ့ အကန့်အသစ်တစ်ခု ဖြစ်တဲ့ Single Inheritance ပုံစံကို ကျော်လွှားနိုင်ဖို့အတွက် ထည့်သွင်းထားတဲ့ Feature တစ်ခု ဖြစ်ပါတယ်။ ကျွန်တော်တို့ PHP မှာ built-in class တွေကို မဖြစ်မနေ Inheritance လုပ်ပြီး ရေးရတာတွေ ရှိပါတယ်။ (ဥပမာ – PDO လို Object တွေပေါ့) အဲဒီလို အခြေအနေမှာ ကျွန်တော်တို့ရေးထားတာတွေကို Inheritance လုပ်ပြီး ရေးလို့ မရတော့ပါဘူး။ အဲဒီလို Multiple Inheritance လုပ်ချင်ပြီဆိုရင်တော့ Trait ကို မဖြစ်မနေ သုံးဖို့ လိုအပ်လာပါလိမ့်မယ်။

Trait Pattern ကို အသုံးပြုနိုင်ဖို့ PHP Version အနေနဲ့ အနည်းဆုံး Version 5.4 ရှိဖို့ လိုအပ်ပါတယ်။ 

<?php
interface AnimalInterface {
    public function eat();
}

trait Bull {
    public $type; 
    public function bite() {
        echo "Bite!<br>";
    }
}
trait Tarrier  {
    public function play() { echo "Play! <br>"; }
}
interface DogInterface {
    public function bark();
}
abstract class Animal implements AnimalInterface {
    public $name; public function eat() {
        echo "EAT! <br>";
    }

    public function __construct($name) {
        $this->name = $name;
        echo "Animal Contruct! <br>";
    }
}
// Inheritance
class Dog extends Animal implements DogInterface {
    use Bull, Tarrier;
    public $foo;
    public function __construct($name, $foo)  {
        parent::__construct($name);
        $this->foo = $foo;
    }
    public function bark() {
        echo "Bark! <br>";
    }
}
$dog = new Dog("Aung Net", "bar");
$dog->type  = "Test";
$dog->bite();
$dog->eat();
$dog->play();
?>

ကျွန်တော်တို့ Bull & Tarrier ကို Trait Pattern သုံးပြီး ရေးလိုက်ပါတယ်။ ပြီးရင် Dog Class ထဲမှာ use ဆိုပြီး ပြန်သုံးလိုက်ပါတယ်။ ပြီးရင်တော့ $dog->play() တို့ $dog->bite() ဆိုတဲ့ method တွေကို သုံးလို့ရပါပြီ။

ဒီနေရာမှာ တစ်ခု သတိထားဖို့လိုတာက namespace တွေနဲ့ သုံးတဲ့အခါ use ဆိုတဲ့ keyword ကို သုံးပါတယ်။ အဲဒါနဲ့ မရောသွားဖို့ လိုအပ်ပါတယ်။ သူက outer class မှာ သုံးရတာ ဖြစ်ပြီး trait မှာတော့ inner class မှာ သုံးရပါတယ်။

Delegation

Delegation ကတော့ Inheritance နဲ့ မတူပါဘူး။ ဥပမာ CEO, GM, Manager ဆိုတဲ့ class တစ်ခုစီ ရှိတယ်ဆိုပါစို့။ Inheritance & Trait ကို သုံးပြီး ရေးမယ်ဆိုရင် အေက်မှာ ပြထားတဲ့အတိုင်း ရေးရပါမယ်။

<?php
interface CeoInterface {
    public function resign();
}
class CEO implements CeoInterface {
    public function resign() {
        echo "Resign! <br>";
    }
}
interface GmInterface {
    public function giveMoney();
}
class GM implements GmInterface {
    public function giveMoney() {
        echo "Give Money! <br>";
    }
}

class Manager extends CEO {
    use GM;
}

$manager = Manager();
$manager->resign();
$manager->giveMoney();
?>

အဲဒီလို ရေးလိုက်မယ်ဆိုရင် အလုပ်တော့ ဖြစ်နေတာ သေချာပါတယ်။ ဒါပေမယ့် SOLID Principle ထဲက Separation of Concern ဆိုတာနဲ့တော့ မကိုက်ညီတော့ပါဘူး။ Manager ဟာ Manager အလုပ်ပဲ လုပ်ရပါမယ်။ CEO အလုပ် လုပ်လို့မရပါဘူး။ GM အလုပ် လုပ်လို့မရပါဘူး။ Inheritance နဲ့ သုံးလိုက်တဲ့အတွက် Manager တစ်ယောက်တည်းမှာ CEO ရဲ့ အလုပ်ရော GM ရဲ့ အလုပ်ပါ လုပ်နေတာ ဖြစ်နေပါတယ်။ အဲဒီလို ရေးလိုက်တဲ့အတွက် Test ပြုလုပ်ရမှာလဲ အခက်အခဲတွေ ရှိသွားစေပါတယ်။ ဒီနေရာမှာ Delegation ပုံစံနဲ့ရေးလိုက်မယ်ဆိုရင်တော့ အဲဒီ အခက်အခဲကို ကျော်လွှားနိုင်ပါလိမ့်မယ်။

<?php
interface CeoInterface {
    public function resign();
}
class CEO implements CeoInterface {
    public function resign() {
        echo "Resign! <br>";
    }
}
interface GmInterface {
    public function giveMoney();
}
class GM implements GmInterface {
    public function giveMoney() {
        echo "Give Money! <br>";
    }
}
class Manager {
    private $ceo;
    private $gm;
    public function __construct(
    	CeoInterface $ceo, 
    	GmInterface $gm) {
        $this->ceo = $ceo;    
        $this->gm = $gm;
    } 
    public function resign() {
        $this->ceo->resign();
    }
    public function giveMoney() {
        $this->gm->giveMoney();
    }
}
$ceo = new CEO();
$gm = new GM();
$manager = new Manager($ceo, $gm);
$manager->resign();
$manager->giveMoney();

?>

အဲဒီလို ရေးလိုက်မယ်ဆိုရင် CEO က CEO အလုပ်လုပ်နေပါလိမ့်မယ်။ GM က GM အလုပ်လုပ်နေပါလိမ့်မယ်။ Manager အနေနဲ့ CEO နဲ့ GM ကို မှီခိုနေတာ ဖြစ်တဲ့အတွက် Dependency ရှိနေပါတယ်။ အဲဒီ Dependency ကို ပြန်သုံးနိုင်ဖို့အတွက် Dependency Injection လုပ်ပေးဖို့ လိုပါတယ်။ ဒီနေရာမှာတော့ Interface Injection ပုံစံကို အသုံးပြုသွားပါတယ်။