Abstract class trong PHP

Abstract class là gì?

Lớp trừu tượng (abstract class) là 1 lớp có những đặc điểm sau:

  • Lớp này có các phương thức trừu tượng.
  • Các lớp khác khi kế thừa lớp trừu tượng sẽ phải định nghĩa các phương thức trừu tượng ấy.
  • Một lớp chỉ có thể kế thừa 1 lớp trừu tượng.

Một lớp trừu tượng là một lớp chứa ít nhất một phương thức trừu tượng. Một phương thức trừu tượng là một phương thức được khai báo, nhưng không được triển khai trong mã.

Abstract class trong PHP

Một lớp hoặc phương thức trừu tượng được định nghĩa với từ khóa abstract:

<?php
    abstract class ParentClass {
        abstract public function someMethod1();
        abstract public function someMethod2($name, $color);
        abstract public function someMethod3() : string;
    }
?>

Khi kế thừa từ một lớp trừu tượng, phương thức lớp con phải được định nghĩa với cùng tên và cùng một chỉ thị truy cập với lớp cha hoặc một chỉ thị truy cập ít hạn chế hơn.

Vì vậy, nếu phương thức trừu tượng được định nghĩa là protected, thì phương thức lớp con phải được định nghĩa là protected hoặc public, không được sử dụng chỉ thị truy cập private.

Ngoài ra, kiểu và số lượng các đối số bắt buộc phải giống nhau. Tuy nhiên, các lớp con có thể bổ sung thêm các đối số tùy chọn.

Vì vậy, khi một lớp con được kế thừa từ một lớp trừu tượng, chúng ta có các quy tắc sau:

  • Phương thức lớp con phải được định nghĩa có cùng tên và nó triển khai mã cho phương thức trừu tượng ở lớp cha.
  • Phương thức lớp con phải được định nghĩa với cùng một chỉ thị truy cập với lớp cha hoặc một chỉ thị truy cập ít hạn chế hơn.
  • Số lượng các đối số bắt buộc phải giống nhau. Tuy nhiên, lớp con có thể bổ sung thêm các đối số tùy chọn.

Ví dụ sau minh họa abstract class trong PHP:

<?php
    // Parent class
    abstract class Car {
        public $name;
        
        public function __construct($name) {
            $this->name = $name;
        }
        
        abstract public function intro() : string;
    }

    // Child classes
    class Audi extends Car {
        public function intro() : string {
            return "Choose German quality! I'm an $this->name!";
        }
    }

    class Volvo extends Car {
        public function intro() : string {
            return "Proud to be Swedish! I'm a $this->name!";
        }
    }

    class Citroen extends Car {
        public function intro() : string {
            return "French extravagance! I'm a $this->name!";
        }
    }

    // Create objects from the child classes
    $audi = new audi("Audi");
    echo $audi->intro();
    echo "<br>";

    $volvo = new volvo("Volvo");
    echo $volvo->intro();
    echo "<br>";

    $citroen = new citroen("Citroen");
    echo $citroen->intro();
?>

Đây là kết quả:

Choose German quality! I'm an Audi!
Proud to be Swedish! I'm a Volvo!
French extravagance! I'm a Citroen!

Các lớp Audi, Volvo và Citroen kế thừa từ lớp trừu tượng Car. Điều này có nghĩa là các lớp Audi, Volvo và Citroen có thể sử dụng thuộc tính public là $name cũng như phương thức public là __construct(), intro() từ lớp Car vì tính kế thừa.

Nhưng phương thức intro() là một phương thức trừu tượng nên các lớp con sẽ phải ghi đè phương thức này và chúng sẽ trả về một chuỗi.

Hãy xem thêm một ví dụ khác trong đó phương thức trừu tượng có một đối số cà có chỉ thị truy cập là protected như sau:

<?php
    abstract class ParentClass {
        // Abstract method with an argument
        abstract protected function prefixName($name);
    }

    class ChildClass extends ParentClass {
        public function prefixName($name) {
            if ($name == "John Doe") {
                $prefix = "Mr.";
            } elseif ($name == "Jane Doe") {
                $prefix = "Mrs.";
            } else {
                $prefix = "";
            }
            return "{$prefix} {$name}";
        }
    }

    $class = new ChildClass;
    echo $class->prefixName("John Doe");
    echo "<br>";
    echo $class->prefixName("Jane Doe");
?>

Đây là kết quả:

Mr. John Doe
Mrs. Jane Doe

Như bạn thấy trong ví dụ trên, lớp trừu tượng ParentClass có một phương thức trừu tượng là prefixName() có một tham số đầu vào là $name và chỉ thị truy cập là protected (chỉ cho phép truy cập ở trong lớp và lớp con).

Lớp ChildClass kế thừa từ lớp trừu tượng ParentClass và nó ghi đè phương thức trừu tượng prefixName() của lớp cha.

Không những vậy, phương thức prefixName ở lớp con còn được cung cấp chỉ thị truy cập là public nên nó có thể truy cập được từ đối tượng của lớp con.

Hãy xem xét một ví dụ khác trong đó phương thức trừu tượng có một đối số và lớp con có hai đối số tùy chọn không được định nghĩa trong phương thức trừu tượng của cha mẹ:

<?php
    abstract class ParentClass {
        // Abstract method with an argument
        abstract protected function prefixName($name);
    }

    class ChildClass extends ParentClass {
        // The child class may define optional arguments that are not in the parent's abstract method
        public function prefixName($name, $separator = ".", $greet = "Dear") {
            if ($name == "John Doe") {
                $prefix = "Mr";
            } elseif ($name == "Jane Doe") {
                $prefix = "Mrs";
            } else {
                $prefix = "";
            }
            return "{$greet} {$prefix}{$separator} {$name}";
        }
    }

    $class = new ChildClass;
    echo $class->prefixName("John Doe");
    echo "<br>";
    echo $class->prefixName("Jane Doe", ".", "Hello");
?>

Đây là kết quả:

Dear Mr. John Doe
Hello Mrs. Jane Doe

Như bạn thấy ở ví dụ trên, lớp con ChildClass đã ghi đè phương thức prefixName() clas lớp cha ParentClass. Ngoài tham số $name là bắt buộc, nó bổ sung thêm hai tham số có giá trị mặc định là $separator và $greet.

Bạn hoàn toàn có thể truyền giá trị cho những tham số mới bổ sung này.



Bài viết liên quan:

Hướng dẫn lập trình PHP toàn tập sẽ giúp bạn từng bước tìm hiểu và nắm vững ngôn ngữ lập trình PHP.

Hướng dẫn cách truy xuất, lọc, sắp xếp dữ liệu MySQL trong PHP sử dụng MySQLi và PDO.

MySQL prepared statements trong PHP rất hữu ích để chống lại các cuộc tấn công SQL Injection.