× Giới thiệu Lịch khai giảng Tin tức Sản phẩm học viên

Khái niệm cơ bản về thiết kế lập trình hướng đối tượng cho ứng dụng PHP

21/03/2024 01:27

Hôm nay chúng ta sẽ xem xét các khái niệm nền tảng về lập trình hướng đối tượng trong PHP — đóng gói, kế thừa, trừu tượng hóa và đa hình — và cách sử dụng các khái niệm này trong việc tạo mã mạnh mẽ, có thể mở rộng và bảo trì.

Nguồn gốc của lập trình hướng đối tượng trong PHP

Nhìn lại năm 1999, thời điểm tôi viết chương trình PHP đầu tiên, lập trình thủ tục là con đường duy nhất để đi. Một tập lệnh PHP điển hình được đan xen chặt chẽ với HTML và phong cách lập trình thủ tục từng dòng một đã phù hợp với giai đoạn phát triển này. Không lâu sau đó, PHP 4 được giới thiệu với lần đầu tiên áp dụng lập trình hướng đối tượng (OOP). Nhưng phải đến PHP 5, việc phát triển PHP hướng đối tượng mới bắt đầu một cách nghiêm túc.

Bắt đầu với PHP 5, ngôn ngữ này cung cấp hỗ trợ OOP chính thức, cho phép các nhà phát triển tạo các lớp, phương thức và thuộc tính cần thiết cho OOP. Các tính năng OOP của PHP cho phép bạn xây dựng các ứng dụng web an toàn và có thể mở rộng bằng cách gói gọn logic liên quan vào các đối tượng. Điều này đặc biệt hữu ích trong bối cảnh phát triển web, trong đó các thành phần mô-đun như hệ thống xác thực người dùng, kết nối cơ sở dữ liệu và chức năng quản lý nội dung có thể được phát triển bằng cách sử dụng các nguyên tắc OOP để tổ chức và tái sử dụng tốt hơn. Cũng thật thú vị khi lưu ý rằng nhiều khung công tác PHP hiện đại, chẳng hạn như Laminas, Laravel và Symfony, được xây dựng xung quanh các khái niệm OOP, khiến kiến ​​thức về OOP trở thành điều cần thiết đối với các nhà phát triển PHP muốn sử dụng các khung này một cách hiệu quả.

Tôi sẽ phải thừa nhận rằng tôi đã bị đưa vào thế giới PHP hướng đối tượng và la hét! Tôi đã từng tranh luận (và vẫn tranh luận) rằng OOP bổ sung thêm chi phí. Tuy nhiên, việc có thể đặt các hàm trong cùng một gói với các biến và hằng là yếu tố thay đổi cuộc chơi về mặt phát triển, đặc biệt là trong các dự án cần mở rộng quy mô.

Nguyên tắc cơ bản của lập trình hướng đối tượng

Trước khi bạn có thể bắt đầu hiểu các mẫu thiết kế phần mềm OOP cổ điển, trước tiên bạn phải hiểu bốn nguyên tắc chính của OOP: Encapsulation, Trừu tượng hóa, Kế thừa và Đa hình. Những nguyên tắc này không chỉ áp dụng cho PHP OOP mà còn cho bất kỳ ngôn ngữ hướng đối tượng nào.

Encapsulation OOP

Encapsulation là một trong những nguyên tắc cơ bản của Lập trình hướng đối tượng (OOP) và nó đề cập đến việc kết hợp dữ liệu với các phương thức hoạt động trên dữ liệu đó hoặc hạn chế quyền truy cập trực tiếp vào một số thành phần của đối tượng. 

Encapsulation là rất quan trọng vì nó cho phép mức độ toàn vẹn dữ liệu cao bằng cách bảo vệ trạng thái bên trong của đối tượng khỏi sự can thiệp và lạm dụng không mong muốn. Trong ngữ cảnh của PHP OOP, tính Encapsulation đảm bảo rằng hoạt động bên trong của đối tượng được ẩn khỏi thế giới bên ngoài và chỉ có giao diện được kiểm soát mới được hiển thị để tương tác. Điều này không chỉ ngăn dữ liệu nội bộ của đối tượng bị thay đổi theo những cách không thể đoán trước mà còn làm cho mã trở nên an toàn và mạnh mẽ hơn trước những thay đổi, vì các chi tiết triển khai có thể phát triển mà không ảnh hưởng đến các phần khác của hệ thống dựa vào đối tượng.

Để triển khai tính năng Encapsulation trong PHP, các nhà phát triển sử dụng các công cụ sửa đổi truy cập: `public`, `protected` và `private` khi khai báo các thuộc tính và phương thức của lớp. Thuộc tính hoặc phương thức `public` có thể được truy cập từ mọi nơi - cả bên trong và bên ngoài lớp. Một thuộc tính hoặc phương thức `được bảo vệ` có thể được truy cập trong chính lớp đó và bằng cách kế thừa và các lớp cha. Tuy nhiên, một thuộc tính hoặc phương thức `private` chỉ có thể truy cập được trong lớp định nghĩa nó. Bằng cách chọn cẩn thận công cụ sửa đổi truy cập thích hợp, bạn kiểm soát mức độ hiển thị của các thuộc tính và phương thức, từ đó Encapsulation dữ liệu của đối tượng.

Nhiều nhà phát triển PHP cũng triển khai các phương thức “getter” và “setter”, là các phương thức công khai cho phép truy cập có kiểm soát vào các thuộc tính riêng tư hoặc được bảo vệ, thực thi thêm việc Encapsulation bằng cách cho phép lớp kiểm soát các giá trị và hoạt động được phép trên các thuộc tính của nó. Cá nhân tôi cho rằng các phương pháp này là một ví dụ khác về sự phình to của OOP và thường bị lạm dụng và không cần thiết. Tuy nhiên, trong một số trường hợp, bạn có thể cần phải tăng cường kiểm soát quyền truy cập vào tài sản. Cũng có thể dữ liệu cần được thao tác thêm, như trong ví dụ này:

// Date and time is presented as a string
public function setDate(string $date)
{
	// You wish the property to be stored as a DateTime instance
	$this→date = new DateTime($date);
}

Encapsulation mang lại nhiều lợi ích cho các ứng dụng PHP. Bằng cách kiểm soát cách truy cập và sửa đổi dữ liệu trong các đối tượng, tính năng Encapsulation sẽ thúc đẩy một cơ sở mã mô-đun và gắn kết hơn. Các bản cập nhật và sửa lỗi cho một lớp thường có thể được thực hiện với tác động tối thiểu đến phần còn lại của ứng dụng, miễn là giao diện chung của lớp đó không thay đổi. Điều này làm cho các ứng dụng PHP dễ bảo trì và mở rộng hơn theo thời gian. Tính năng Encapsulation cũng tăng cường tính bảo mật vì các phần quan trọng của mã được ẩn khỏi quyền truy cập từ bên ngoài, giảm nguy cơ giả mạo do vô tình hoặc cố ý. Hơn nữa, mã được Encapsulation có nhiều khả năng tái sử dụng hơn vì các chi tiết triển khai được tách biệt khỏi giao diện hiển thị, cho phép các nhà phát triển PHP tận dụng các lớp giống nhau trên các phần khác nhau của ứng dụng hoặc trong các dự án hoàn toàn khác nhau mà không gặp phải các tác dụng phụ không mong muốn.

Trừu tượng

Trong lập trình, tính trừu tượng là khái niệm chuyển trọng tâm từ các chi tiết và chi tiết cụ thể về cách thực hiện một điều gì đó lên một cấp độ cao hơn, nơi chúng ta có thể nghĩ về các ý tưởng một cách tổng quát hơn. 

Nó giúp giảm độ phức tạp và cho phép các lập trình viên tập trung vào các tương tác ở cấp độ cao hơn. Trong PHP, lớp trừu tượng là một lớp không thể tự khởi tạo và được sử dụng làm bản thiết kế cho các lớp khác. 

Lớp trừu tượng

Các lớp trừu tượng được khai báo bằng từ khóa trừu tượng và được sử dụng để định nghĩa các phương thức phải được triển khai bởi bất kỳ lớp con nào mở rộng lớp trừu tượng.

Dưới đây là một số điểm chính về các lớp trừu tượng trong PHP:

  • Khai báo: Một lớp trừu tượng được định nghĩa bằng cách thêm từ khóa vào trước tên lớp abstract.
abstract class Connect 
{
    // Abstract methods and properties
}
  • Mở rộng: Để sử dụng một lớp trừu tượng, một lớp con phải mở rộng nó và cung cấp các triển khai cho các phương thức trừu tượng.
class PdoConnect extends Connect
{
    /** 
     * Provides a PDO database connection
     */
    public function connect(array $params)
    {
        [$dsn, $user, $pwd, $opts] = $params;
        return new PDO($dsn, $user, $pwd, $opts);
    }
}
  • Khởi tạo : Bạn không thể trực tiếp tạo một thể hiện của một lớp trừu tượng.
$connect = new Connect($params); // This will cause an error
$connect = new PdoConnect($params); // Works
  • Mục đích: Mục đích chính của lớp trừu tượng là cung cấp định nghĩa chung về lớp cơ sở mà nhiều lớp dẫn xuất có thể chia sẻ. Trọng tâm của lớp trừu tượng PHP là xây dựng hệ thống phân cấp kế thừa của các lớp.

Giao diện

Một giao diện trong PHP được sử dụng để định nghĩa một tập hợp các phương thức mà các lớp triển khai phải tuân theo. Các giao diện tương tự như các lớp trừu tượng ở chỗ chúng không thể được khởi tạo và bất kỳ lớp nào triển khai giao diện đều phải triển khai tất cả các phương thức của nó. Tuy nhiên, các giao diện không thể có các thuộc tính hoặc triển khai phương thức đầy đủ, trong khi các lớp trừu tượng thì có.

•    Khai báo: Một giao diện được xác định bằng interfacetừ khóa.

interface EncryptInterface 
{
    public function encrypt(string $text) : string;
}
  • Triển khai: Một lớp triển khai giao diện phải sử dụng implementstừ khóa và phải cung cấp các triển khai cụ thể cho tất cả các phương thức của giao diện.
class Login implements EncryptInterface 
{
    // mandated by EncryptInterface
    public function encrypt(string $text) : string 
    {
        return password_hash($text, PASSWORD_DEFAULT);
    }
}
  • Nhiều giao diện: Một lớp có thể triển khai nhiều giao diện, đây là một cách để đạt được nhiều kế thừa trong PHP. Điều này là do giao diện tự chèn vào hệ thống phân cấp kế thừa ngay phía trên lớp triển khai dưới dạng siêu lớp giả.

    class Login implements EncryptInterface, VerifyInterface
    {
        // mandated by EncryptInterface
        public function encrypt(string $text) : string 
        {
            return password_hash($text, PASSWORD_DEFAULT);
        }
        // also implements the method or methods mandated
        // by VerifyInterface
    }
  • Mục đích: Mục đích chính của giao diện là cung cấp một hợp đồng chính thức mà các lớp triển khai phải tuân theo, đảm bảo rằng chúng trình bày một bộ phương thức cụ thể với thế giới bên ngoài. Các giao diện PHP độc lập và không bị ràng buộc với hệ thống phân cấp kế thừa, khiến chúng cực kỳ linh hoạt và hữu ích.

Sự khác biệt giữa các lớp trừu tượng và giao diện

  Các lớp học Giao diện
Thực hiện Có thể có cả phương pháp trừu tượng và cụ thể với việc thực hiện đầy đủ Chỉ có thể có các phương thức trừu tượng
Của cải Có thể có thuộc tính Không thể có thuộc tính
Hằng số Có thể có hằng số Có thể có chi phí
Di sản Có thể mở rộng một lớp trừu tượng (kế thừa đơn), nhưng nó có thể triển khai nhiều giao diện (nhiều kế thừa giả). không có
Hiển thị phương pháp Có thể có khả năng hiển thị lớp công khai và được bảo vệ Tất cả các phương pháp phải được công khai
Mục đích thiết kế Được sử dụng khi các lớp có chung cơ sở hoạt động Được sử dụng khi thiết lập giao diện chung cho các lớp khác nhau bất kể hệ thống phân cấp kế thừa.

Kế thừa OOP

Kế thừa là một khái niệm cơ bản trong lập trình hướng đối tượng (OOP) cho phép một lớp, được gọi là lớp con hoặc lớp con, kế thừa các thuộc tính, phương thức và hành vi từ một lớp khác, được gọi là lớp cha hoặc lớp cha. 

Cơ chế này cho phép sử dụng lại mã và thiết lập mối quan hệ giữa các lớp trong đó lớp con là phiên bản chuyên biệt của lớp cha. Lớp con có thể thêm các tính năng độc đáo của riêng nó hoặc ghi đè hành vi của lớp cha, điều này thúc đẩy tổ chức các lớp có thứ bậc. Kế thừa thể hiện mối quan hệ "is a", nghĩa là lớp con là một loại của siêu lớp. Điều này dẫn đến sự sắp xếp mã trực quan hơn và có thể giảm đáng kể sự dư thừa, vì các chức năng dùng chung chỉ cần được viết một lần trong siêu lớp.

Sử dụng tính kế thừa trong PHP để mở rộng lớp

Trong PHP, tính kế thừa được triển khai bằng cách sử dụng từ khóa mở rộng trong khai báo lớp. Khi một lớp mở rộng một lớp khác, nó sẽ kế thừa tất cả các thuộc tính và phương thức không riêng tư của lớp cha. Điều này cho phép lớp con tận dụng và mở rộng chức năng được xác định trong lớp cha. Ví dụ: nếu bạn có một lớp HashBase với các thuộc tính cơ bản như 'thuật toán' và 'hashr' và các phương thức như 'makeHash()' và 'getAlgorithm()', thì bạn có thể mở rộng lớp này để tạo các kiểu băm cụ thể hơn, như HashWithMd5 hoặc HashWithSha256, sẽ tự động có quyền truy cập vào các thuộc tính và phương thức của lớp HashBase. Lớp con cũng có thể định nghĩa các thuộc tính hoặc phương thức bổ sung, cũng như ghi đè các phương thức kế thừa để cung cấp các hành vi chuyên biệt.

class HashBase
{
    public string $algorithm = ‘none’;
    public string $hash = ‘’;
    public function makeHash(string $text) : string
    {
        $this->hash = $text;
        return $this->hash;
    }
    public function getAlgorithm() : string
    {
        return $this->algorithm;
    }
}

class HashWithMD5 extends HashBase
{
    public string $algorithm = ‘md5’;
    public function makeHash(string $text) :  void
    {
        $this->hash = md5($text);
        return $this->hash;
    }
}

Thực tiễn tốt nhất để sử dụng tính kế thừa trong PHP

Khi sử dụng tính kế thừa trong PHP, có một số phương pháp hay nhất bạn nên xem xét để tạo mã sạch, có thể bảo trì và có thể mở rộng:

  • Sử dụng tính kế thừa một cách tiết kiệm: Chỉ nên sử dụng tính kế thừa khi có mối quan hệ rõ ràng và hành vi chung. Việc lạm dụng tính kế thừa có thể dẫn đến hệ thống phân cấp phức tạp, khó hiểu và khó duy trì.
  • Ưu tiên Thành phần hơn Kế thừa: Trong nhiều trường hợp, việc sử dụng thành phần (bao gồm các đối tượng của các lớp khác làm thuộc tính) có thể linh hoạt hơn tính kế thừa. Điều này thường được gọi là mối quan hệ "có một".
  • Được bảo vệ thay vì riêng tư: Nếu bạn cho rằng một thuộc tính hoặc phương thức nên được ẩn khỏi công chúng nhưng vẫn có thể truy cập được đối với các lớp con, hãy sử dụng chế độ được bảo vệ thay vì khả năng hiển thị riêng tư. Điều này đảm bảo tính Encapsulation trong khi vẫn cho phép kế thừa.
  • Nguyên tắc thay thế Liskov (LSP): Khi ghi đè các phương thức, hãy đảm bảo rằng các phương thức của lớp con có thể thay thế một cách an toàn các phương thức của lớp cha. Điều này có nghĩa là chúng phải chấp nhận các tham số đầu vào giống nhau và trả về cùng loại.
  • Tránh phân cấp kế thừa sâu: Kế thừa lồng nhau sâu có thể trở thành vấn đề và khó theo dõi. Thay vào đó, hãy hướng tới một hệ thống phân cấp nông cạn và xem xét các mẫu thiết kế khác, như giao diện, để mang lại sự linh hoạt.

Bằng cách tuân thủ những thực tiễn này, bạn có thể tận dụng toàn bộ tiềm năng kế thừa trong PHP để tạo ra một thiết kế hướng đối tượng mạnh mẽ và có thể mở rộng.

Đa hình OOP

Đa hình là một khái niệm cơ bản trong lập trình hướng đối tượng (OOP) cho phép các đối tượng thuộc các lớp khác nhau được coi là đối tượng của một siêu lớp chung. 

Thuật ngữ "đa hình" xuất phát từ các từ tiếng Hy Lạp "poly" (có nghĩa là nhiều) và "morph" (có nghĩa là hình thức), và về cơ bản nó cho phép thực hiện nhiều hình thức hoặc hành vi. Trong ngữ cảnh OOP, điều này có nghĩa là một hàm hoặc phương thức duy nhất có thể hoạt động trên nhiều loại đối tượng khác nhau. Nó cho phép các đối tượng có cấu trúc bên trong khác nhau chia sẻ cùng một giao diện bên ngoài. Điều này đặc biệt hữu ích khi nhiều lớp chia sẻ một tập hợp hành vi chung nhưng thực hiện chúng theo những cách khác nhau. Tính đa hình thúc đẩy tính linh hoạt và khả năng bảo trì trong mã bằng cách cho phép bạn viết các thành phần tổng quát hơn và có thể tái sử dụng.

Triển khai tính đa hình trong PHP

Trong PHP, tính đa hình được triển khai thông qua tính kế thừa và giao diện. Khi một lớp kế thừa từ một lớp khác, nó có thể ghi đè các phương thức của lớp cha để cung cấp các cách triển khai cụ thể. Đây là một dạng đa hình được gọi là đa hình lớp con. PHP cũng cho phép đa hình thông qua các giao diện. Giao diện là một hợp đồng chỉ định những phương thức mà một lớp phải triển khai mà không cung cấp cách triển khai phương thức đó. Các lớp triển khai cùng một giao diện có thể được sử dụng thay thế cho nhau trong mã vì chúng đảm bảo sự hiện diện của các phương thức được xác định bởi giao diện.

interface ShapeInterface
{
    public function calculateArea();
}

class Circle implements ShapeInterface 
{
    protected $radius;

    public function __construct($radius) 
    {
        $this->radius = $radius;
    }

    public function calculateArea() 
    {
        return pi() * pow($this->radius, 2);
    }
}

class Rectangle implements ShapeInterface 
{
    protected $width;
    protected $height;

    public function __construct($width, $height) 
    {
        $this->width = $width;
        $this->height = $height;
    }

    public function calculateArea() 
    {
        return $this->width * $this->height;
    }
}

$print = function (ShapeInterface $shape) 
{
    echo 'The area is: ' . $shape->calculateArea();
}
$print = function (ShapeInterface $shape)
{
    echo 'The area is: ' . $shape->calculateArea() . PHP_EOL;
};

$print(new Circle(9));
$print(new Rectangle(9,9));

// Actual Output:
/*
The area is: 254.46900494077
The area is: 81
*/

Trong ví dụ trên, cả hai Circleđều Rectanglethực hiện Shapegiao diện. Hàm $printẩn danh có thể chấp nhận bất kỳ trường hợp nào của Shape, cho dù đó là CircleRectanglehoặc bất kỳ hình dạng nào khác có thể được xác định trong tương lai, thể hiện tính đa hình.

Sức mạnh của tính đa hình trong thiết kế ứng dụng

Tính đa hình cực kỳ mạnh mẽ trong thiết kế ứng dụng vì nó thúc đẩy việc sử dụng giao diện cấp cao và trừu tượng hơn. Điều này khuyến khích sự phát triển của mã linh hoạt và dễ bảo trì hơn. Nó cho phép các nhà phát triển viết các chương trình xử lý các đối tượng có chung siêu lớp một cách thống nhất, ngay cả khi mỗi lớp con có cách triển khai rất khác nhau. Điều này đặc biệt có lợi trong các hệ thống lớn nơi thường xuyên thay đổi vì nó giảm thiểu tác động của những thay đổi. Ví dụ: hệ thống xử lý thanh toán có thể sử dụng tính đa hình để xử lý các phương thức thanh toán khác nhau. Mỗi phương thức thanh toán, như thẻ tín dụng, PayPal hoặc chuyển khoản ngân hàng, có thể được triển khai dưới dạng một lớp riêng biệt tuân theo giao diện PaymentMethod chung. Phần còn lại của hệ thống có thể tương tác với bất kỳ phương thức thanh toán nào nhờ hỗ trợ đa hình của PHP, cho phép dễ dàng bổ sung hoặc sửa đổi các phương thức thanh toán mà không ảnh hưởng đến logic xử lý cốt lõi.

Source: https://www.zend.com/blog/object-oriented-programming-php