+2

Lập trình hướng đối tượng (OOP) trong PHP

OOP (Lập trình hướng đối tượng) là một phương thức lập trình tập trung vào các đối tượng, với mong muốn có thể "lập trình hóa" được các đối tượng ở ngoài đời thực vào trong ứng dụng được lập trình. Từ đó có thể giúp cho công việc lập trình trở nên dễ dàng hơn, bảo mật hơn và áp dụng được những nguyên tắc lập trình giúp cho source code tốt hơn. Một vài nguyên tắc trong lập trình hướng đối tượng có thể xem tại bài viết: Link

Từ phiên bản PHP 5, PHP cung cấp khả năng lập trình theo hướng đối tượng mạnh mẽ và là kiểu lập trình thường thấy nhất trong các ứng dụng PHP hiện đại ngày nay.

Các thành phần cơ bản trong PHP dành cho OOP

Class và Object

Class và object là 2 thành phần cơ bản nhất trong lập trình hướng đối tượng. Tất cả các khái niệm, định nghĩa điều xoay quanh 2 khái niệm cốt lõi này. Đơn giản có thể hiểu Class là một bản thiết kế để tạo ra một đối tượng (object), và object là một phiên bản đối tượng được tạo ra từ class.
Ví dụ minh họa như là xây một ngôi nhà, thì bản vẽ của kiến trúc sư chính trong trường hợp này chính là Class, còn ngôi nhà được xây dựng theo bản thiết kế đó chính là object. Và ta cũng thấy được là một bản thiết kế thì có thể xây hàng loạt các ngôi nhà có cấu trúc giống nhau trên nhiều mảnh đất khác nhau (mảnh đất có thể xem như bộ nhớ). Và cứ mỗi khi xây một ngôi nhà, nếu ta cung cấp một số tùy chỉnh khác nhau như màu sơn nhà, vật liệu dùng cho cửa, ... thì ngôi nhà được tạo ra sẽ có thể khác so với các ngôi nhà trước đó dù là dùng chung một bản thiết kế (Class).
Trong PHP thì cấu trúc của Class và Object như sau:

// Định nghĩa class
class House {
	private $color;
	private $door;
}

// Khởi tạo đối tượng từ class
$house = new House();

Method và Property

Method (phương thức) là định nghĩa các "hành động" mà chúng ta cung cấp cho đối tượng có thể sử dụng. Còn Property (thuộc tính) là định nghĩa về các đặc tính mà đối tượng sở hữu.
Quay lại ví dụ về Class House ở trên, màu sơn hay chất liệu cửa là thuộc tính còn hành động như xây nhà là một phương thức.

class House {
	// property
	private $color;
	private $door;

	public function build() // method
	{
		// Action
	}
}

// Khởi tạo đối tượng từ class
$house = new House();
$house->build(); // Đối tượng áp dụng hành động

Một điểm lưu ý là bất cứ method hay property nào của Class đều có một phạm vi truy cập nhất định (private, protected, public)

Constructor/ Destructor

Constructor

Khi tạo ra một Object từ Class, ta có thể cung cấp các giá trị để định nghĩa các thuộc tính của đối tượng mà ta muốn tạo ra từ Class đó. Từ đó mỗi đối tượng tạo ra đều có thể tùy chỉnh được dù là tạo ra từ chung 1 Class (chung bản thiết kế).
VD: Khi xây ngôi nhà chúng ta muốn một ngôi nhà có màu xanh còn 1 ngôi nhà khác giống hệt vậy nhưng có màu đỏ

class House {
	// property
	private $color;
	private $door;

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

	public function build() // method
	{
		// Action
	}
}

// Khởi tạo đối tượng từ class
$blue_house = new House('blue');
$red_house = new House('red');

Destructor

Ngược lại với Constructor thì Destructor được sử dụng để dọn dẹp các thứ thừa thải khi object bị "phá hủy" (kết thúc vòng đời của một object).
VD: Như khi xây một ngôi nhà và ở một thời gian thì không cần dùng đến ngôi nhà đó nữa. Người chủ (phần mềm) sẽ phải phá hủy ngôi nhà đó đi và dọn dẹp các tàn dư của ngôi nhà sau khi phá hủy (hàm Destructor được gọi) khỏi khu đất đó (bộ nhớ) để sử dụng khu đất đó cho mục đích khác.
Trong PHP vì một vòng đời của object là khá ngắn nên phương thức này ít được sử dụng.

Magic Methods

Magic Method là những phương thức đặt biệt được PHP hỗ trợ xây dựng sẵn khi làm việc với Class. Các phương thức này sẽ được gọi khi một sự kiện (event) nào đó được diễn ra xung quanh object được xây dựng bởi Class.

Một số Magic Method thường dùng:

Construct và Destruct

Như đã đề cập về chức năng ở trên, Construct là magic method được gọi khi một đối tượng mới được khởi tạo. Còn Destruct là một magic method sẽ được gọi khi đối tượng đó không còn trong hệ thống.

Get và Set

Magic method __get()__set() sẽ được gọi khi truy cập vào một thuộc tính không cho phép truy cập (scope private hoặc protected) hoặc truy cập vào một thuộc tính không tồn tại của đối tượng.
Đây là hai method hữu ích cho việc truy cập vào thuộc tính private của đối tượng.
VD:

// Source: www.geeksforgeeks.org
class MagicMethod {
    function __get($name){
        echo "You are trying to get '" . $name .
          "' which is either inaccessible
           or non existing member";
    }
    function __set($name , $value) {
        echo "You are trying to modify '"
          . $name . "' with '" . $value .
          "' which is either inaccessible
          or non-existing member";
    }
}

$obj = new MagicMethod();
$obj->value; // You are trying to get 'value' which is either inaccessible or non existing member
$obj->value = "Hello"; // You are trying to modify 'value' with 'Hello' which is either inaccessible or non-existing member

toString

Magic method này sẽ được gọi khi có hành động thay đổi (convert) object thành string.
VD:

$home = new Home();
echo $home; // Method __toString() của class Home sẽ được gọi

Invoke

Magic method này sẽ được gọi khi sử dụng object như một hàm.
VD:

$home = new Home();
$home(); // Method __invoke() của class Home sẽ được gọi

Call

Magic method này sẽ gọi khi gọi một method chưa được định nghĩa hoặc không có quyền truy cập.
VD:

$home = new Home();
$home->notDefinedMethod(); // Method __call() được định nghĩa trong class Home sẽ được gọi

Inheritance (kế thừa)

Một đặc điểm quan trọng nhất của lập trình hướng đối tượng (OOP) chính là tính kế thừa. Nhờ có tính chất này mà các đoạn code có thể được tái sử dụng lại khi các Class con kế thừa từ Class cha.
VD:

class Human
{
	public function talk()
	{
		echo 'Talking';
	}
}

class Student extends Human
{}

$student = new Student();
$student->talk(); // Kế thừa phương thức từ lớp cha

Một điểm cần lưu ý đó là PHP chỉ hỗ trợ đơn kế thừa, nghĩa là không thể kế thừa 2 class cha từ một class con

Abstract Class

Abtract Class là một class có ít nhất một abstract method, method này được khai báo định nghĩa tên cũng như tham số truyền vào ở class cha nhưng không được thiết lập cách xử lý chức năng. Công việc thiết lập này do class con đảm nhiệm (implement code). VD:

abstract class Parrent 
{
	abstract protected function method($param);
}

class Child extends Parrent
{
	public function method($param) // implement code abstract method
	{
		echo $param;
	}
}

Vì có thể định nghĩa nhưng không implement chức năng nên abstract class thường được dùng để tạo mẫu (pattern) cho nhiều class kế thừa chúng và để các class con đó implement chức năng của abstract method đó. Một khái niệm cũng có nhiệm vụ khá tương tự đó là interface.

Interface

Interface như một bảng mẫu cho các class, interface sẽ định nghĩa các phương thức và tham số của phương thức đó nhưng không bao gồm code xử lý. Các class implement interface đó sẽ phải khai báo và implement code xử lý các phương thức mà interface đã định nghĩa.
VD:

interface ClassInterface
{
	public function methodA();
	public function methodB($param);
}

class A implements ClassInterface
{
	public function methodA() {
		echo 'A';
	}
	public function methodB($param) {
		echo 'B'
	}
}

Khác với việc kế thừa từ abstract class, mỗi class chỉ được kế thừa 1 abstracts class thì trong PHP một class có thể implement nhiều interface.
Lưu ý là khi implement 1 interface thì bắt buộc class đó phải implement hết tất cả các method mà interface đó khai báo.

Trait

Với đặc điểm đơn kế thừa (một class chỉ kế thừa được một class cha) thì sẽ có một vài trường hợp các class cần có các method khác nhau thuộc nhiều class khác nhau để sử dụng. Vì vậy PHP đã hỗ trợ Trait để giải quyết điều đó.
Trait sẽ cung cấp các method mà cần được sử dụng lại trong nhiều class khác nhau, và khi class nào cần sử dụng các method của trait thì chỉ cần dùng khai báo trait đó trong class.
VD:

trait ExampleTrait
{
	public function methodA()
	{
		echo 'A';
	}
}

class ExampleClass
{
	use ExampleTrait;
}

$object = new ExampleClass();
$object->methodA(); // A

Cách áp dụng OOP vào PHP tốt hơn

Chú ý đến Exception Handling

Trong bất kì một ngôn ngữ lập trình nào thì việc xử lý lỗi là một vấn đề tất yếu cần được xử lý. Xử lý lỗi giúp cho có thể điều khiển được các hành động khi có những ngoại lệ không mong muốn xảy ra.
Trong PHP cung cấp các hàm try catch và class Exception hỗ trợ cho việc xử lý lỗi.
VD:

// Source: https://www.w3schools.com
function checkNum($number) {
  if($number>1) {
    throw new Exception("Value must be 1 or below");
  }
  return true;
}

//trigger exception in a "try" block
try {
  checkNum(2);
  //If the exception is thrown, this text will not be shown
  echo 'If you see this, the number is 1 or below';
}

//catch exception
catch(Exception $e) {
  echo 'Message: ' .$e->getMessage();
}

Chú ý xử lý đầy đủ các trường hợp lỗi sẽ giúp cho hệ thống vận hành tốt hơn và tránh các trường hợp không mong muốn xảy ra. Nhất là trong lập trình hướng đối tượng khi mà sự kế thừa, áp dụng interface thường làm cho sự phức tạp của source code tăng lên rất nhiều từ đó rất nhiều trường hợp lỗi có thể xảy ra.

Áp dụng các coding convention

Như việc thiết kể bản vẽ để xây nhà thì các kiến trúc sư cần tuân theo các quy tắc được đề ra từ trước của ngành để giúp cho các kiến trúc sư khác cũng như là người thi công có thể hiểu được bản vẽ. Thì việc áp dụng các coding convention giúp cho code của chúng ta theo một tiêu chuẩn quy ước chung, theo một khuôn mẫu trình bày chung, giúp cho người khác có thể đọc code một cách dễ dàng hơn và code được tinh gọn, dễ đọc hơn. Việc debug là rất quan trọng trong lập trình nên viết code dễ đọc và theo tiêu chuẩn đã đề ra là một cách để giảm thiểu thời gian debug.
Một coding convention phổ biến của PHP có thể kể đến là PSR-12. Đồng thời mỗi framework hay CMS cũng có thể có một số coding convention riêng cần được tuân thủ theo.

Áp dụng các Design Pattern

Design Pattern có thể xem như là những khuôn mẫu, cách thiết kế code để đạt được độ tối ưu và tuân thủ theo các nguyên tắc lập trình (Một số nguyên tắc lập trình) đã được những người đi trước, các bậc thầy về lập trình nghiên cứu đưa ra và được các lập trình viên ủng hộ, sử dụng.
Design Pattern là một mảng kiến thức nội dung rộng lớn. Đã có rất nhiều Design Pattern được tạo ra phục vụ cho các mục đích khác nhau. Để nghiên cứu tìm hiểu về Design Pattern thì mọi người có thể tìm hiểu thông qua cuốn sách Head First Design Patterns, 2nd Edition

Nguồn tài liệu, ví dụ tốt nhất về lập trình hướng đối tượng trong PHP

Để học tập một phương pháp lập trình thì tìm hiểu các ví dụ của những bậc thầy đi trước là cách nhanh và hiệu quả nhất. Với lập trình hướng đối tượng thì những khái niệm và ví dụ như trên là những điều cơ bản nhất và muốn hiểu sâu hơn thì phải đọc thêm rất nhiều các đoạn code trong các dự án để hiểu rõ hơn cách áp dụng trong thực tế là như thế nào.
Theo tôi, nguồn tài liệu, ví dụ tốt nhất nên tìm hiểu chính là các framework nổi tiếng của PHP ví dụ như Laravel, hoặc một CMS như Magento chẳng hạn. Đây là những dự án có cốt lõi rất hay và áp dụng rất nhiều khái niệm OOP vào code. Đọc và hiểu cách áp dụng OOP của các dự án này sẽ giúp ta hiểu rõ hơn rất nhiều về OOP. Tuy nhiên là các framework và CMS của PHP đều có một độ phức tạp của code là rất cao nên đòi hỏi nhiều công sức và thời gian để tìm hiểu, có thể không phù hợp với những người mới bắt đầu.

Tài liệu tham khảo


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.