Tính Đóng Gói - OOP PHP

Tính đóng gói (encapsulation) "đóng gói" thuộc tính và phương thức của đối tượng (hoặc lớp) thông qua việc giới hạn quyền truy cập (hoặc thay đổi) giá trị của thuộc tính hoặc quyền gọi phương thức. Nói cách khác tính đóng gói cho phép kiểm soát quyền truy cập (và thay đổi) giá trị của thuộc tính hoặc quyền gọi phương thức của đối tượng (hoặc lớp) và đối tượng (hoặc lớp) con.

Trong PHP việc đóng gói được thực hiện nhờ sử dụng các từ khoá publicprivate và protected:

  • public: Cho phép truy cập (và thay đổi giá trị) của thuộc tính và phương thức ở mọi phạm vi
  • private: Chỉ cho phép truy cập (hay thay đổi) giá trị của thuộc tính và phương thức ở phạm vi của đối tượng (hoặc lớp).
  • protected: Chỉ cho phép truy cập (hay thay đổi) giá trị của thuộc tính và phương thức ở phạm vi của đối tượng con (hoặc lớp con).

Hãy xem xét một tình huống thực tế cần áp dụng tính đóng gói đó là khi bạn muốn rút tiền từ tài khoản ngân hàng của bạn (giả thiết rằng bạn tới phòng giao dịch của ngân hàng để rút mà không sử dụng máy ATM). Và khi tới ngân hàng thì nhân viên ở đó sẽ yêu cầu bạn cung cấp các giấy tờ liên quan để kiểm tra thông tin trước khi rút tiền cho bạn. Như vậy ở đây bạn không trực tiếp rút tiền từ tài khoản của mình mà phải thông qua nhân viên ngân hàng (khác với trường hợp bạn để tiền trong ví bạn luôn có thể trực tiếp lấy tiền ra bất cứ lúc nào).

Hãy xem tình huống trên được áp dụng như thế nào trong lập trình hướng đối tượng với tính đóng gói.

Đầu tiên chúng ta có danh sách các tài khoản với thông tin cá nhân là địa chỉ email và mật khẩu như sau:

<?php
class BankAccount {
    public $listAccounts = [
        [
            'email' => 'peter@example.net',
            'pass' => 'peter123',
        ],
        [
           'email' => 'mary@example.net',
            'pass' => 'mary123',
        ]
    ];
}

Tiếp theo chúng ta định nghĩa một lớp BankAccount với 3 thuộc tính là $email$pass và $balance. Trong đó chúng ta giới hạn quyền truy cập của thuộc tính $balance sử dụng từ khoá private.

class BankAccount {
    public $email;
    public $pass;
    private $_balance;
}

Với từ khoá public thì quyền truy cập và thay đổi giá trị của thuộc tính có thể thực hiện ở ngoài lớp. Ví dụ:

$peter = new BankAccount;
$peter->email = 'peter@example.net'; // hợp lệ
echo $peter->email; // hợp lệ

 

Tuy nhiên với từ khoá là private thì quyền truy cập và thay đổi giá trị của thuộc tính chỉ có thể thực hiện được ở bên trong lớp.

$peter = new BankAccount;
$peter->email = 'peter@example.net';

$peter->_balance = 10000000; // báo lỗi
echo $peter->_balance; // báo lỗi

 

Để truy cập và thay đổi giá trị của biến có phạm vi là private thì bạn cần thông qua một phương thức public. Ví dụ như sau:

class BankAccount {
    public $email;
    public $pass;
    private $_balance;

    public function setBalance($balance) {
        $this->_balance = $balance;
    }

    public function getBalance() {
        return $this->_balance;
    }
}

Lúc này chúng ta có thể thực hiện việc truy cập và gán giá trị cho các đối tượng tạo ra từ lớp BankAccount sử dụng 2 phương thức tương ứng là getBalance() và setBalance().

$peter = new BankAccount;
$peter->email = 'peter@example.net';
$peter->setBalance(10000000);
echo $peter->getBalance(); // hợp lệ

 

Để hiểu tại sao chúng ta cần phải "phức tạp hoá" vấn đề nên như vậy thì bây giờ bạn thêm một phương thức authenticate() để kiểm tra thông tin người dùng cung cấp có hợp lệ hay không:

class BankAccount {
    // ...
    public function authenticate() {
        foreach ($listAccounts as $account) {
            if (($account['email'] == $this->email) && ($account['pass'] == $this->pass)) {
                return true;
            }
        }
        return false;
    }
}

Phương thức authenticate() trên sẽ kiểm tra xem địa chỉ email và mật khẩu người dùng cung cấp có khớp với thông tin của bất cứ tài khoản nào trong danh sách hay không và trả về kết quả là true hoặc false.

Bây giờ bạn thay đổi phương thức setBalance() thành như sau:

class BankAccount {
    public function setBalance($balance) {
        if (!$this->authenticate()) {
            echo"Error!";
        }
        $this->_balance = $balance;
    }
}

Đoạn câu lệnh điều kiện if thêm vào dùng để kiểm tra xem người dùng này là hợp lệ hay không. Nếu dùng chúng ta mới cho phép thay đổi giá trị số dư tài khoản $balance ngược lại chúng ta thông báo lỗi:

$peter = new BankAccount;
$peter->email = 'peter@example.net';
$peter->pass = 'wrong pass';

$peter->setBalance(10000000); // Error

$peter->pass = 'peter123';
$peter->setBalance(10000000); // cho phép
echo $peter->getBalance(); // cho phép

Như vậy qua ví dụ trên bạn hiểu được tại sao chúng ta phải đóng gói các thuộc tính trong đối tượng. Tính đóng gói cho phép chúng ta thực hiện việc "giấu" thông tin và qua đó giảm bớt sự phức tạp khi lập trính. Nó giống như khi bạn thiết kế một chiếc điều khiển tivi bạn đưa cho người dùng chiếc remote và hướng dẫn họ cách sử dụng. Người dùng chỉ cần phải bấm một số trên remote để chuyển kênh. Mọi sự phức tạp trong quy trình xử lý khi người dùng chuyển kênh được "giấu" bên trong thiết kế của chiếc remote.