extends
Để Xây Dựng Ứng Dụng Mạnh MẽBạn muốn hiểu rõ về kế thừa trong PHP và cách sử dụng từ khóa extends
? Bài viết này cung cấp một hướng dẫn chi tiết, từ khái niệm cơ bản, cú pháp, khả năng truy cập (public, protected, private), đến ghi đè phương thức và từ khóa final
. Khám phá cách kế thừa giúp tái sử dụng mã, mở rộng chức năng và tối ưu hóa cấu trúc ứng dụng OOP của bạn!
Kế thừa là một trong bốn trụ cột cơ bản của Lập trình Hướng đối tượng (OOP), cùng với Đóng gói (Encapsulation), Trừu tượng (Abstraction) và Đa hình (Polymorphism). Trong PHP, cơ chế kế thừa được triển khai thông qua từ khóa extends
, cho phép một lớp (lớp con - child class) kế thừa các thuộc tính (properties) và phương thức (methods) từ một lớp khác (lớp cha - parent class hoặc base class).
Định nghĩa:
Kế thừa là cơ chế cho phép một lớp mới được tạo ra dựa trên một lớp đã tồn tại. Lớp mới (lớp con) sẽ "thừa hưởng" tất cả các đặc tính (thuộc tính) và hành vi (phương thức) của lớp cha, đồng thời có thể định nghĩa thêm các đặc tính và hành vi riêng của mình, hoặc ghi đè (override) các hành vi của lớp cha.
Tại sao nó quan trọng?
extends
Trong PHP, để một lớp kế thừa từ một lớp khác, bạn sử dụng từ khóa extends
sau tên lớp con, theo sau là tên lớp cha.
<?php
// Lớp cha (Parent Class / Base Class)
class Animal {
public $name;
protected $sound;
public function __construct($name, $sound) {
$this->name = $name;
$this->sound = $sound;
}
public function eat() {
return $this->name . " is eating.";
}
public function makeSound() {
return $this->name . " says " . $this->sound . ".";
}
}
// Lớp con (Child Class) kế thừa từ Animal
class Dog extends Animal {
public function __construct($name) {
parent::__construct($name, "Woof!"); // Gọi constructor của lớp cha
}
public function fetch() {
return $this->name . " is fetching the ball.";
}
}
// Lớp con khác kế thừa từ Animal
class Cat extends Animal {
public function __construct($name) {
parent::__construct($name, "Meow!");
}
// Ghi đè phương thức makeSound() của lớp cha
public function makeSound() {
return $this->name . " purrs gently: " . $this->sound;
}
}
// Sử dụng các lớp
$dog = new Dog("Buddy");
echo $dog->eat() . "<br>"; // Kế thừa từ Animal
echo $dog->makeSound() . "<br>"; // Kế thừa từ Animal
echo $dog->fetch() . "<br>"; // Phương thức riêng của Dog
echo "<br>";
$cat = new Cat("Whiskers");
echo $cat->eat() . "<br>"; // Kế thừa từ Animal
echo $cat->makeSound() . "<br>"; // Ghi đè phương thức
// echo $cat->fetch(); // Sẽ gây lỗi, Cat không có phương thức fetch()
?>
Giải thích:
Animal
là lớp cha. Nó có các thuộc tính $name
, $sound
và các phương thức eat()
, makeSound()
.Dog
và Cat
là các lớp con. Chúng sử dụng extends Animal
để kế thừa từ Animal
.Dog
và Cat
, chúng ta sử dụng parent::__construct()
để gọi constructor của lớp cha. Điều này rất quan trọng để khởi tạo các thuộc tính được định nghĩa trong lớp cha.Dog
kế thừa eat()
và makeSound()
từ Animal
và thêm phương thức riêng fetch()
.Cat
cũng kế thừa eat()
, nhưng nó ghi đè (override) phương thức makeSound()
để cung cấp một triển khai khác.Trong PHP, khả năng truy cập của các thuộc tính và phương thức (public
, protected
, private
) ảnh hưởng đến cách chúng được kế thừa và truy cập.
public
: Các thuộc tính và phương thức public
có thể được truy cập từ bất cứ đâu, bao gồm cả các lớp con và bên ngoài lớp.protected
: Các thuộc tính và phương thức protected
chỉ có thể được truy cập bên trong lớp định nghĩa chúng và các lớp con kế thừa từ chúng. Chúng không thể truy cập trực tiếp từ bên ngoài lớp.private
: Các thuộc tính và phương thức private
chỉ có thể được truy cập bên trong lớp định nghĩa chúng. Chúng không thể truy cập từ các lớp con hoặc bên ngoài lớp.
<?php
class BaseClass {
public $publicProp = "Public property";
protected $protectedProp = "Protected property";
private $privateProp = "Private property";
public function publicMethod() {
return "Public method called.";
}
protected function protectedMethod() {
return "Protected method called.";
}
private function privateMethod() {
return "Private method called.";
}
public function accessBasePrivate() {
return $this->privateProp . " from base class.";
}
}
class ChildClass extends BaseClass {
public function accessParentMembers() {
echo $this->publicProp . "<br>"; // Có thể truy cập public
echo $this->protectedProp . "<br>"; // Có thể truy cập protected
// echo $this->privateProp; // Lỗi: Không thể truy cập private
echo $this->publicMethod() . "<br>"; // Có thể truy cập public method
echo $this->protectedMethod() . "<br>"; // Có thể truy cập protected method
// echo $this->privateMethod(); // Lỗi: Không thể truy cập private method
}
}
$child = new ChildClass();
$child->accessParentMembers();
echo $child->publicProp . "<br>"; // Truy cập public từ bên ngoài
// echo $child->protectedProp; // Lỗi: Không thể truy cập protected từ bên ngoài
// echo $child->privateProp; // Lỗi: Không thể truy cập private từ bên ngoài
echo $child->publicMethod() . "<br>"; // Truy cập public method từ bên ngoài
// echo $child->protectedMethod(); // Lỗi: Không thể truy cập protected method từ bên ngoài
// echo $child->privateMethod(); // Lỗi: Không thể truy cập private method từ bên ngoài
?>
Khi một lớp con định nghĩa một phương thức có cùng tên với một phương thức trong lớp cha, phương thức của lớp con sẽ "ghi đè" (override) phương thức của lớp cha khi được gọi trên một đối tượng của lớp con.
<?php
class Vehicle {
public function start() {
return "Vehicle is starting.";
}
public function stop() {
return "Vehicle is stopping.";
}
}
class Car extends Vehicle {
// Ghi đè phương thức start()
public function start() {
return "Car is starting with ignition.";
}
// Thêm phương thức mới
public function drive() {
return "Car is driving.";
}
}
$vehicle = new Vehicle();
echo $vehicle->start() . "<br>"; // Output: Vehicle is starting.
$car = new Car();
echo $car->start() . "<br>"; // Output: Car is starting with ignition. (Đã bị ghi đè)
echo $car->stop() . "<br>"; // Output: Vehicle is stopping. (Vẫn giữ nguyên từ lớp cha)
echo $car->drive() . "<br>"; // Output: Car is driving.
?>
Trong ví dụ trên, khi bạn gọi $car->start()
, PHP sẽ thực thi phương thức start()
được định nghĩa trong lớp Car
, chứ không phải của lớp Vehicle
.
final
Từ khóa final
có thể được sử dụng để ngăn chặn việc ghi đè phương thức hoặc kế thừa lớp.
final public function methodName()
: Khi một phương thức được đánh dấu là final
, nó không thể bị ghi đè bởi bất kỳ lớp con nào.final class ClassName
: Khi một lớp được đánh dấu là final
, nó không thể bị kế thừa bởi bất kỳ lớp nào khác.
<?php
class Shape {
final public function calculateArea() {
// Logic tính diện tích chung
return "Calculating area...";
}
public function getDescription() {
return "This is a generic shape.";
}
}
// class Circle extends Shape {
// // Sẽ gây lỗi nếu cố gắng ghi đè phương thức final
// // public function calculateArea() {
// // return "Circle area calculation.";
// // }
// }
// final class MyFinalClass {
// public function hello() {
// return "Hello from final class!";
// }
// }
// // Sẽ gây lỗi nếu cố gắng kế thừa lớp final
// // class AnotherClass extends MyFinalClass {
// // }
?>
__construct
)Khi một lớp con được khởi tạo, constructor của lớp con sẽ được gọi. Nếu bạn muốn chạy constructor của lớp cha (ví dụ: để khởi tạo các thuộc tính kế thừa), bạn phải gọi nó một cách tường minh bằng parent::__construct()
.
<?php
class Person {
protected $name;
public function __construct($name) {
$this->name = $name;
echo "Person constructor called for " . $this->name . "<br>";
}
public function getName() {
return $this->name;
}
}
class Student extends Person {
private $studentId;
public function __construct($name, $studentId) {
parent::__construct($name); // Rất quan trọng để gọi constructor của lớp cha
$this->studentId = $studentId;
echo "Student constructor called for " . $this->name . " with ID " . $this->studentId . "<br>";
}
public function getStudentInfo() {
return $this->getName() . " (ID: " . $this->studentId . ")";
}
}
$student = new Student("Alice", "S12345");
echo $student->getStudentInfo() . "<br>";
?>
Mặc dù kế thừa là một công cụ mạnh mẽ, nhưng nó cũng có một số hạn chế:
Kế thừa lý tưởng cho các trường hợp khi có mối quan hệ "là một" (is-a relationship) rõ ràng giữa các đối tượng. Ví dụ:
Car
là một Vehicle
.Dog
là một Animal
.Circle
là một Shape
.Nếu mối quan hệ là "có một" (has-a relationship), thì Composition (tập hợp) thường là lựa chọn tốt hơn.
Kế thừa với từ khóa extends
là một tính năng cốt lõi của Lập trình Hướng đối tượng trong PHP, giúp tái sử dụng mã, mở rộng chức năng và tạo ra cấu trúc phân cấp rõ ràng cho ứng dụng. Việc hiểu rõ cách sử dụng extends
cùng với các quy tắc về khả năng truy cập, ghi đè phương thức và từ khóa final
là rất quan trọng để xây dựng các ứng dụng PHP mạnh mẽ, dễ bảo trì và mở rộng. Tuy nhiên, cũng cần lưu ý đến các hạn chế để tránh lạm dụng và dẫn đến thiết kế kém hiệu quả.