Lập trình PHP (Bài 3)

Bài 3: Tìm hiểu sự kế thừa và tầm vực trong lập trình hướng đối tượng

Ở bài trước, chúng ta đã đề cập tới các phương thức magic trong hướng đối tượng. Bởi việc sử dụng các phương thức này. Mã nguồn của chúng ta sẽ trở nên linh động và giảm bớt các thao tác không cần thiết khi vận hành. Trong bài này, chúng tôi tiếp tục giới thiệu tới các bạn yếu tố kế thừa, tầm vực của phương thức và thuộc tính trong hướng đối tượng.
Sự kế thừa trong hướng đối tượng

Một Class có thể kế thừa các phương thức và thuộc tính của class khác, bằng cách sử dụng từ khóa extends. Ví dụ, để tạo ra một class kế thừa MyClass đồng thời thêm vào nó một phương thức, bạn sẽ có mã lệnh như sau:

<?php 

class MyClass 
{ 
    public $prop1 = "I'm a class property!"; 

    public function __construct() 
    { 
        echo 'The class "', __CLASS__, '" was initiated!<br />'; 
    } 

    public function __destruct() 
    { 
        echo 'The class "', __CLASS__, '" was destroyed.<br />'; 
    } 

    public function __toString() 
    { 
        echo "Using the toString method: "; 
        return $this->getProperty(); 
    } 

    public function setProperty($newval) 
    { 
        $this->prop1 = $newval; 
    } 

    public function getProperty() 
    { 
        return $this->prop1 . "<br />"; 
    } 
} 

class MyOtherClass extends MyClass 
{ 
    public function newMethod() 
    { 
        echo "From a new method in " . __CLASS__ . ".<br />"; 
    } 
} 

// Tạo đối tượng 
$newobj = new MyOtherClass; 

// Echo object ra dạng chuỗi 
echo $newobj->newMethod(); 

// Sử dụng một phương thức của class cha 
echo $newobj->getProperty(); 

?>

Mở trình duyệt, và xem kết quả xuất ra như sau:

The class “MyClass” was initiated!
From a new method in MyOtherClass.
I’m a class property!
The class “MyClass” was destroyed.

Kỹ thuật nạp chồng trong Hướng Đối Tượng

Tại một class con, để thay đổi giá trị, tính năng của một thuộc tính hoặc phương thức đã có sẵn ở class cha, bạn chỉ cần ghi đè (nạp chồng) lên nó bằng cách khởi tạo lại chính nó trong class con:

<?php 

class MyClass 
{ 
    public $prop1 = "I'm a class property!"; 

    public function __construct() 
    { 
        echo 'The class "', __CLASS__, '" was initiated!<br />'; 
    } 

    public function __destruct() 
    { 
        echo 'The class "', __CLASS__, '" was destroyed.<br />'; 
    } 

    public function __toString() 
    { 
        echo "Using the toString method: "; 
        return $this->getProperty(); 
    } 

    public function setProperty($newval) 
    { 
        $this->prop1 = $newval; 
    } 

    public function getProperty() 
    { 
        return $this->prop1 . "<br />"; 
    } 
} 

class MyOtherClass extends MyClass 
{ 
    public function __construct() 
    { 
        echo "A new constructor in " . __CLASS__ . ".<br />"; 
    } 

    public function newMethod() 
    { 
        echo "From a new method in " . __CLASS__ . ".<br />"; 
    } 
} 

// Tạo object 
$newobj = new MyOtherClass; 

// Echo object ra dưới dạng chuỗi 
echo $newobj->newMethod(); 

// Sử dụng một phương thức của class cha 
echo $newobj->getProperty(); 
?>
Kết quả dữ liệu xuất ra trên trình sẽ thay đổi như sau:
A new constructor in MyOtherClass.
From a new method in MyOtherClass.
I’m a class property!
The class “MyClass” was destroyed.
Kỹ thuật bảo vệ phương thức trong phương pháp nạp chồng
Để thêm một chức năng mới trong một phương thức kế thừa mà vẫn giữ được nguyên gốc chức năng ban đầu của nó, bạn sử dụng từ khóa parent cùng với toán tử phân giải phạm vi (scope resolution operator)(::):
<?php 

class MyClass 
{ 
    public $prop1 = "I'm a class property!"; 

    public function __construct() 
    { 
        echo 'The class "', __CLASS__, '" was initiated!<br />'; 
    } 

    public function __destruct() 
    { 
        echo 'The class "', __CLASS__, '" was destroyed.<br />'; 
    } 

    public function __toString() 
    { 
        echo "Using the toString method: "; 
        return $this->getProperty(); 
    } 

    public function setProperty($newval) 
    { 
        $this->prop1 = $newval; 
    } 

    public function getProperty() 
    { 
        return $this->prop1 . "<br />"; 
    } 
} 

class MyOtherClass extends MyClass 
{ 
    public function __construct() 
    { 
        parent::__construct(); // Gọi hàm dựng từ class cha 
        echo "A new constructor in " . __CLASS__ . ".<br />"; 
    } 

    public function newMethod() 
    { 
        echo "From a new method in " . __CLASS__ . ".<br />"; 
    } 
} 

// Tạo object 
$newobj = new MyOtherClass; 

// Echo object ra dưới dạng chuỗi 
echo $newobj->newMethod(); 

// Sử dụng một phương thức của class cha 
echo $newobj->getProperty(); 

?>
Kết quả sẽ xuất ra giá trị của cả phương thức constructor của class cha lẫn giá trị phương thức constructor của class con:
The class “MyClass” was initiated!
A new constructor in MyOtherClass.
From a new method in MyOtherClass.
I’m a class property!
The class “MyClass” was destroyed.
Gán tầm vực cho Thuộc Tính và Phương Thức
Để gia tăng khả năng kiểm soát các object, các phương thức và thuộc tính sẽ được gán thêm những giá trị tầm vực. Nó cho phép chúng ta kiểm soát khả năng truy cập (như thế nào và ở nơi đâu) của thuộc tính và phương thức. Chúng ta có ba từ khóa để đảm nhiệm việc này:public,protected, vàprivate. Ngoài ra, còn có một từ khóa nữa cũng có khả năng đảm nhiệm việc này, đó là static, nó cho phép các thuộc tính và phương thức có thể được truy cập mà không cần khởi tạo class.
Chú ý:tầm vực của thuộc tính và phương thức là 2 tính năng mới của PHP 5.
Thuộc Tính và Phương Thức Mang Tầm Vực Public

Với nhóm này, toàn bộ các phương thức và thuộc tính bạn sử dụng đều mang tính công cộng. Tức là chúng có thể được truy cập từ bất cứ nơi đâu, cà trong và ngoài class.

Khi bạn khai báo một thuộc tính hay phương thức mà không gán tầm vực cho chúng, thì PHP tự hiểu nó thuộc nhóm public.

Thuộc Tính và Phương Thức Mang Tầm Vực Protected

Khi một thuộc tính hay phương thức được khai báo tầm vựcprotected, chúng chỉ có thể truy cập được trong chính class chứa chúng, hoặc tại một class kế thừa class này.

Gán tầm vực protected cho phương thức getProperty() trong MyClass và thử truy cập nó từ bên ngoài class:

<?php 

class MyClass 
{ 
    public $prop1 = "I'm a class property!"; 

    public function __construct() 
    { 
        echo 'The class "', __CLASS__, '" was initiated!<br />'; 
    } 

    public function __destruct() 
    { 
        echo 'The class "', __CLASS__, '" was destroyed.<br />'; 
    } 

    public function __toString() 
    { 
        echo "Using the toString method: "; 
        return $this->getProperty(); 
    } 

    public function setProperty($newval) 
    { 
        $this->prop1 = $newval; 
    } 

    protected function getProperty() 
    { 
        return $this->prop1 . "<br />"; 
    } 
} 

class MyOtherClass extends MyClass 
{ 
    public function __construct() 
    { 
        parent::__construct(); 
        echo "A new constructor in " . __CLASS__ . ".<br />"; 
    } 

    public function newMethod() 
    { 
        echo "From a new method in " . __CLASS__ . ".<br />"; 
    } 
} 

// Tạo object 
$newobj = new MyOtherClass; 

// Thử gọi một phương thức protected 
echo $newobj->getProperty(); 

?>
Chạy script, và bạn sẽ nhận được thông báo lỗi như sau:
The class “MyClass” was initiated!
A new constructor in MyOtherClass.
Fatal error: Call to protected method MyClass::getProperty() from context ” in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 55
Nào, bây giờ chúng ta thử tạo một phương thức mới trong MyOtherClass để gọi phương thức getProperty():
<?php 

class MyClass 
{ 
    public $prop1 = "I'm a class property!"; 

    public function __construct() 
    { 
        echo 'The class "', __CLASS__, '" was initiated!<br />'; 
    } 

    public function __destruct() 
    { 
        echo 'The class "', __CLASS__, '" was destroyed.<br />'; 
    } 

    public function __toString() 
    { 
        echo "Using the toString method: "; 
        return $this->getProperty(); 
    } 

    public function setProperty($newval) 
    { 
        $this->prop1 = $newval; 
    } 

    protected function getProperty() 
    { 
        return $this->prop1 . "<br />"; 
    } 
} 

class MyOtherClass extends MyClass 
{ 
    public function __construct() 
    { 
        parent::__construct(); 
        echo "A new constructor in " . __CLASS__ . ".<br />"; 
    } 

    public function newMethod() 
    { 
        echo "From a new method in " . __CLASS__ . ".<br />"; 
    } 

    public function callProtected() 
    { 
        return $this->getProperty(); 
    } 
} 

// Tạo object 
$newobj = new MyOtherClass; 

// Gọi phương thức protected từ một phương thức public 
echo $newobj->callProtected(); 

?>
Và kết quả sẽ đúng như bạn mong muốn:
The class “MyClass” was initiated!
A new constructor in MyOtherClass.
I’m a class property!
The class “MyClass” was destroyed.
Thuộc Tính và Phương Thức Mang Tầm Vực Private
Một thuộc tính hay phương thức được gán private thì chỉ có thể truy cập được bên trong class đã định nghĩa nó. Điều này có nghĩa là ngay cả khi có 1 class kế thừa class đã định nghĩa một thuộc tính private, thuộc tính hay phương thức này cũng sẽ không tồn tại trong toàn bộ class con.Để chứng minh điều này, chúng ta sẽ khai báo phương thức getProperty() là private trong MyClass, và cố gắng truy cập nó thông qua một phương thức public callProtected()từ MyOtherClass(class kế thừa MyClass):

<?php 

class MyClass 
{ 
    public $prop1 = "I'm a class property!"; 

    public function __construct() 
    { 
        echo 'The class "', __CLASS__, '" was initiated!<br />'; 
    } 

    public function __destruct() 
    { 
        echo 'The class "', __CLASS__, '" was destroyed.<br />'; 
    } 

    public function __toString() 
    { 
        echo "Using the toString method: "; 
        return $this->getProperty(); 
    } 

    public function setProperty($newval) 
    { 
        $this->prop1 = $newval; 
    } 

    private function getProperty() 
    { 
        return $this->prop1 . "<br />"; 
    } 
} 

class MyOtherClass extends MyClass 
{ 
    public function __construct() 
    { 
        parent::__construct(); 
        echo "A new constructor in " . __CLASS__ . ".<br />"; 
    } 

    public function newMethod() 
    { 
        echo "From a new method in " . __CLASS__ . ".<br />"; 
    } 

    public function callProtected() 
    { 
        return $this->getProperty(); 
    } 
} 

// Tạo object 
$newobj = new MyOtherClass; 

// Sử dụng một phương thức từ class con 
echo $newobj->callProtected(); 
?>
Reload lại trình duyệt để xem thông báo lỗi như sau xuất hiện:
The class “MyClass” was initiated!
A new constructor in MyOtherClass.
Fatal error: Call to private method MyClass::getProperty() from context ‘MyOtherClass’ in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 49
Thuộc Tính và Phương Thức Static Một phương thức hay thuộc tính được gán tầm vực static có thể truy cập được ngay cả khi bạn không khởi tạo class, bạn chỉ cần cung cấp tên class, toán tử phân giải phạm vi (::), và tên thuộc tính hoặc phương thức.

“Một trong những lợi ích chính khi sử dụng thuộc tính static là chúng giữ các giá trị được lưu trữ trong suốt khoảng thời gian script tồn tại.”

Để chứng minh điều này, chúng ta sẽ thêm vào MyClass một thuộc tính static được gọi là $count và một phương thức static được gọi là plusOne(). Sau đó xây dựng một vòng lặp do…while để xuất ra các giá trị tăng dần mà nhỏ hơn 10 của $count:
<?php 

class MyClass 
{ 
    public $prop1 = "I'm a class property!"; 

    public static $count = 0; 

    public function __construct() 
    { 
        echo 'The class "', __CLASS__, '" was initiated!<br />'; 
    } 

    public function __destruct() 
    { 
        echo 'The class "', __CLASS__, '" was destroyed.<br />'; 
    } 

    public function __toString() 
    { 
        echo "Using the toString method: "; 
        return $this->getProperty(); 
    } 

    public function setProperty($newval) 
    { 
        $this->prop1 = $newval; 
    } 

    private function getProperty() 
    { 
        return $this->prop1 . "<br />"; 
    } 

    public static function plusOne() 
    { 
        return "The count is " . ++self::$count . ".<br />"; 
    } 
} 

class MyOtherClass extends MyClass 
{ 
    public function __construct() 
    { 
        parent::__construct(); 
        echo "A new constructor in " . __CLASS__ . ".<br />"; 
    } 

    public function newMethod() 
    { 
        echo "From a new method in " . __CLASS__ . ".<br />"; 
    } 

    public function callProtected() 
    { 
        return $this->getProperty(); 
    } 
} 

do 
{ 
    // Gọi plusOne mà không khởi tạo MyClass 
    echo MyClass::plusOne(); 
} while ( MyClass::$count < 10 ); 

?>
Chú ý: Khi truy cập các thuộc tính static,ký tự độ la ($) sẽ nằm sau toán tử phân giải phạm vi (::).Khi bạn load script này trên trình duyệt, nó sẽ xuất ra kết quả như sau:
The count is 1.
The count is 2.
The count is 3.
The count is 4.
The count is 5.
The count is 6.
The count is 7.
The count is 8.
The count is 9.
The count is 10.