Encapsulation

ในบทนี้ คุณจะได้เรียนรู้เกี่ยวกับ Encapsulation ในภาษา PHP ซึ่งเป็นคุณสมบัติที่สำคัญในการเขียนโปรแกรมเชิงวัตถุอย่างหนึ่ง และแนวคิดของการห่อหุ้มของข้อมูลในการประยุกต์ใช้จริงกับชีวิตประจำวัน

Encapsulation คืออะไร

Encapsulation คือคุณสมบัติในการปกปิดข้อมูลโดยเป็นกลไกลในการกำหนดการเข้าถึงข้อมูลในคลาสจากภายนอก เช่น ตัวแปรออบเจ็คที่สร้างจากคลาส ในการเขียนโปรแกรมเชิงวัตถุนั้น ตัวแปร เมธอดและโค้ดการทำงานภายในจะถูกห่อห้มไว้ในออบเจ็ค ในภาษาเขียนโปรแกรมเชิงวัตถุรวมทั้งภาษา PHP นั้นใช้ตัวกำหนดการเข้าถึง (Access specifier) ซึ่งเป็นคำสั่งในการกำหนดระดับการเข้าถึงข้อมูลภายในออบเจ็ค เช่น คำสั่ง public protected และ private

OOP Encapsulation การห่อหุ้มข้อมูล

Encapsulation สามารถพบได้ในชีวิตประจำวัน ยกตัวอย่างเช่น Vending machine คุณต้องใส่จำนวนเงิน และเลือกเครื่องดื่มของคุณในการซื้อ แนวคิดของ Encapsulation คือขั้นตอนการทำงานนั้นถูกซ่อนไว้จากผู้ใช้ เช่น การนับเงินหรือทอนเงินของเครื่อง การควบคุมเพื่อที่จะให้ได้เครื่องดื่มที่คุณต้องการออกมา หรือจำนวนเครื่องดื่มทั้งหมดที่เหลืออยู่ ซึ่งข้อมูลและการทำงานนี้ถูกปกปิดสำหรับผู้ใช้ที่จะเห็น และในขณะเดียวกันก็มีการทำงานที่ผู้ใช้สามารถทำได้คือ การหยอดเหรียญและการเลือกเครื่องดื่ม ซึ่งเป็นช่องทางในการติดต่อและทำงานกับข้อมูลภายใน

Access specifiers

Access specifiers คือคำสั่งในการกำหนดการเข้าถึงที่เราใช้กับการประกาศตัวแปรภายในคลาสหรือเรียกอีกอย่างหนึงว่า Property Visibility ในภาษา PHP มี Access specifier อยู่ด้วยกัน 3 แบบดังนี้

  • public - ตัวแปรหรือเมธอดสามารถเข้าถึงได้จากทุกที่ในโปรแกรม
  • protected - ตัวแปรหรือเมธอดสามารถเข้าถึงได้จากในคลาสเดียวกันหรือคลาสที่ได้รับการสืบทอด (Inheritance)
  • private - ตัวแปรหรือเมธอดสามารถเข้าถึงได้จากในคลาสเดียวเท่านั้น

ซึ่ง Access specifiers เหล่านี้เป็นสิ่งที่กำหนดสำหรับการเข้าถึงข้อมูลภายในออบเจ็ค คำสั่ง private และ protected เป็นคำสั่งในการปิดปิดข้อมูล เพราะว่าเราไม่สามารถเข้าถึงสมาชิกประเภทนี้จากภายนอกของออบเจ็คได้ และ public เป็นตัวแทนในการติดต่อหรือการทำงานระหว่างภายในและภายนอกออบเจ็ค

ตัวอย่างของ Encapsulation กับ Vending machine

ต่อไปเราจะยกตัวอย่างการเขียนโปรแกรมด้วยแนวคิดของ Encapsulation ในภาษา PHP กับการทำงานของ Vending machine

<?php

class VendingMachine {

    private $machineMoney = 0;
    private $your_money = 0;

    private $beverage = [
        "A" => ["price" => 6, "name" => "Coke"],
        "B" => ["price" => 6, "name" => "Pepsi"],
        "C" => ["price" => 8, "name" => "Wine"],
        "D" => ["price" => 10, "name" => "Beer"]
    ];

    function __construct() {
        $this->welcome();
    }

    private function welcome() {
        echo "WELCOME TO VENDING MACHINE\n";
        echo "What do you want to drink?\n";

        echo "Code\tName\tPrice\n";
        foreach ($this->beverage as $key => $value) {
            echo "$key\t" . $value["name"] . "\t" . $value["price"] . " USD\n";
        }
        echo "\n";
    }

    public function insertCoin($coin) {
        $this->yourMoney+= $coin;
        echo "Inserted $coin USD, you have $this->yourMoneyUSD.\n";
    }

    public function getBeverage($code) {
        if (array_key_exists($code, $this->beverage)) {
            $bev = $this->beverage[$code];
            if ($this->pay($bev["price"])) {        
                $this->sendBeverage($bev["name"], 
                $this->getChange());

            } else {
                echo "Not enough money, please insert more.\n";
            }
        } else
            echo "You have entered invalid beverage code.\n";
    }

    private function pay($price) {
        if ($this->yourMoney>= $price) {
            $this->yourMoney-= $price;
            $this->machineMoney += $price;
            return true;
        } else {
            return false;
        }
    }

    private function getChange() {
        $change = $this->yourMoney;
        $this->yourMoney= 0;
        return $change;
    }

    private function sendBeverage($name, $change) {
        echo "Please take your beverage ($name) and $changeUSD back.\n";
        echo "Thank you.\n\n";
    }

}

$vender = new VendingMachine();
$vender->insertCoin(5);
$vender->insertCoin(5);
$vender->getBeverage("C");

$vender->insertCoin(4);
$vender->getBeverage("A");
$vender->insertCoin(2);
$vender->getBeverage("A");

?>

ในตัวอย่าง เป็นโปรแกรมในการจำลองการทำงานของ Vending machine ในภาษา PHP โดยโปรแกรมจะมีการให้ผู้ใช้ใส่เงินเข้าไปในเครื่องและเลือกเครื่องดื่มที่ต้องการ ซึ่งการทำงานทั้งหมดถูกกำหนดไว้ในคลาส VendingMachine นั่นหมายความว่าทุกๆ ภายในคลาสทั้งตัวแปรและเมธอดนั้นถูกปกปิดจากออบเจ็คภายนอก (Encapsulated)

private $machineMoney = 0;
private $yourMoney = 0;

private $beverage = [
    "A" => ["price" => 6, "name" => "Coke"],
    "B" => ["price" => 6, "name" => "Pepsi"],
    "C" => ["price" => 8, "name" => "Wine"],
    "D" => ["price" => 10, "name" => "Beer"]
];

ในคลาสมีตัวแปรสามตัวคือ $machineMoney เป็นตัวแปรในการเก็บจำนวนของ Vending machine $yourMoney เป็นตัวแปรในการเก็บจำนวนเงินที่ผู้ใช้ใส่เข้ามา และ $beverage เป็นตัวแปรเก็บข้อมูลของเครื่องดื่ม โดยมีรหัสของเครื่องดื่มเป็น Key ของอาเรย์

ต่อมาเป็นการประกาศฟังก์ชันในการทำงานของ Vending machine

private function welcome() {
    echo "WELCOME TO VENDING MACHINE\n";
    echo "What do you want to drink?\n";

    echo "Code\tName\tPrice\n";
    foreach ($this->beverage as $key => $value) {
        echo "$key\t" . $value["name"] . "\t" . $value["price"] . " USD\n";
    }   
    echo "\n";  
}

เมธอด welcome() เป็นเมธอดในการแสดงข้อความต้นรับในตอนที่ออบเจ็คถูกสร้าง เมธอดนี้ถูกเรียกโดย Constructor และแสดงรายละเอียดของเครื่องดื่มที่สามารถชื้อได้

public function insertCoin($coin) {
    $this->yourMoney+= $coin;
    echo "Inserted $coin USD, you have $this->yourMoneyUSD.\n";
}

เมธอด insertCoin() เป็นเมธอดในการใส่เหรียญเข้าไปในเครื่อง มันเป็นเมธอดในการติดต่อกับภายนอกของคลาส เราได้กำหนดระดบการเข้าถึงของเมธอดนี้เป็น public

public function getBeverage($code)
{
    if (array_key_exists($code, $this->beverage)) {
        $bev = $this->beverage[$code];
        if ($this->pay($bev["price"])) {        
            $this->sendBeverage($bev["name"], 
            $this->getChange());

        } else {
            echo "Not enough money, please insert more.\n";
        }
    } else
        echo "You have entered invalid beverage code.\n";
}

เมธอด getBeverage() เป็นเมธอดในการติดต่อกับภายนอกคลาส (ผู้ใช้) เช่นเดียวกัน ซึงเราได้กำหนดระดับการเข้าถึงเป็น public เมธอดทำการตรวจสอบเครื่องดื่มที่มีอยู่ ทำการจ่ายเงิน และทำการส่งเครื่องดื่มและเงินทอนกลับไปให้กับผู้ใช้

ทั้งเมธอด insertCoin() และ getBeverage() เป็นเมธอดในการติดต่อกับผู้ใช้จากภายนอก เช่น คุณไม่สามารถที่จะเข้าไปวางเงินหรือหยิบเครื่องดื่มออกมาโดยตรงได้ เพราะว่ามันไม่มองเห็นได้โดยคุณจากภายนอก สิ่งเดียวที่คุณทำได้คือการยอดเหรียญและเลือกเครื่องดื่มเพื่อที่จะได้มันมา และการทำงานต่างๆ จะเกิดขึ้นอัตโนมัติภายในคลาส ดังนั้นสองเมธอดนี้ถูกกำหนดเป็น public เพราะมันใช้เชื่อมต่อกับผู้ใช้ (ภายนอกคลาส)

private function pay($price) {
    if ($this->yourMoney>= $price) {
        $this->yourMoney-= $price;
        $this->machineMoney += $price;
        return true;
    } else {
        return false;
    }
}

เมธอด pay() เป็นเมธอดในการจัดการเครื่องการจ่ายเงิน และคำนวณเงินที่เหลือของผู้ใช้ เมธอดนี้มีระดับการเข้าถึงเป็น private เพราะว่ามันเป็นการทำงานเบื้องหลังที่ถูกเรียกภายในคลาส และไม่สามารถเข้าถึงได้จากภานนอก

เมธอด getChange() เป็นเมธอดในการคำนวณเงินที่ต้องทอน และเมธอด sendBeverage() เป็นเมธอดในการส่งเครื่องดื่มและเงินทอดกลับไปยังผู้ซื้อและการซื้อถือเป็นการสำเร็จ เมธอดเหล่านี้เป็นการทำงานภายใน ดังนั้นไม่มันสามารถเข้าถึงจากภายนอกได้

$vender = new VendingMachine();
$vender->insertCoin(5);
$vender->insertCoin(5);
$vender->getBeverage("C");

$vender->insertCoin(4);
$vender->getBeverage("A");
$vender->insertCoin(2);
$vender->getBeverage("A");

กลับมายังเรือง Encapsulation ของเรา เราได้สร้างตัวแปรออบเจ็ค $vender ซึ่งออบเจ็คนี้หมายถึง Vending machine ที่ผู้ใช้สามารถมีปฏิสัมพันธ์ได้ ซึ่งในบริบทของผู้ใช้พวกเขาสามารถเข้าถึงได้เพียง เมธอด insertCoin() และเมธอด getBeverage() สำหรับสิ่งที่เขาต้องการ

ถึงแม้ว่าเมธอดหรือตัวแปรอื่นๆ นั้นไม่สามารถเข้าถึงได้ แต่มันก็ไม่สำคัญกับสิ่งที่เขาต้องการเพราะเมธอดเหล่านั้นทำงานอยู่เบื้องหลังเพื่อให้งานหลักสำเร็จ ดังนั้นสามาชิกที่เป็น private นั้นจึงถูกซ่อนอย่างสมบูรณ์จากภายนอก ซึ่งมันจะถูกเรียกใช้จากเมธอดที่เป็น public แทน

Inserted 5 USD, you have 5 USD.
Inserted 5 USD, you have 10 USD.
Please take your beverage (Wine) and 2 USD back.
Thank you.

Inserted 4 USD, you have 4 USD.
Money not enough, please insert more.
Inserted 2 USD, you have 6 USD.
Please take your beverage (Coke) and 0 USD back.
Thank you.

นี่เป็นการทำงานของโปรแกรม Vending machine

Encapsulation จึงเป็นการพัฒนาโปรแกรมกำหนดการเข้าถึงขึ้นกับความจำเป็นและวัตถุประสงค์การใช้งาน ยกตัวอย่างเช่น ในหน้าปัดแสดงผลของเครื่องบิน จะแสดงข้อมูลทีจำเป็นสำหรับนักบินที่ต้องใข้ในการบินเท่านั้น ซึ่งในความจริงนั้นมีข้อมูลและการทำงานมากมากมายที่ถูกซ่อนอยู่ เบื้องหลังของหน้าปัดนั้น

Encapsulation ถูกนำไปใช้กับทุกการพัฒนาของการเขียนโปรแกรมเชิงวัตถุ ฟังก์ชันและเมธอดเป็นสิ่งหนึ่งที่ถูกห่อหุ้มการทำงานเอาไว้ เช่น เมื่อเราเรียกใช้คำสั่ง echo เราทราบว่าต้องใส่พารามิเตอร์เพื่อที่จะแสดงผล แต่โดยทั่วไปแล้วเราไม่ทราบถึงวิธีการทำงานในขั้นตอนการเชื่อมต่อกับฮาร์ดแวร์ Output buffer หรืออื่นๆ เพราะว่าทำงานเหล่านั้นถูกปกปิดและไม่จำเป็นสำหรับผู้ใช้งาน

ในบทนี้ คุณเรียนรู้เกี่ยวกับแนวคิดของ Encapsulation และตัวอย่างการใช้งานกับการเขียนโปรแกรมเชิงวัตถุเพื่อห่อหุ้มหรือปกปิดข้อมูลและการทำงาน ในภาษา PHP