Trait trong PHP

LAMP Mar 24, 2021

Hẳn là mọi người đã quá quen thuộc với Framework Laravel, hiện là PHP Framework phổ biến được nhiều developer sử dụng nhất hiện nay. Mình cũng là một PHP developer hay sử dụng framework Laravel. Tuy nhiên, có một vấn đề mình đã gặp phải trong dự án đang làm đó là làm thế nào để sử dụng lại các phương thức ở các class khác nhiều lần trong khi PHP là ngôn ngữ chỉ hỗ trợ đơn kế thừa ? Và...Trait đã sinh ra để giải quyết vấn đề này. Vì vậy, hôm nay mình muốn chia sẻ chút kiến thức mà mình học được trong thực tế về việc ứng dụng Trait.

Nội dung bài viết này chúng ta sẽ cùng tìm hiểu Trait là gì ? Nó làm việc như thế nào? Nó khác Abstract ClassInterface ra sao? Tại sao chúng ta nên dùng, đồng thời đưa ra bài toán ứng dụng.

  1. Trait là gì ?

Trong PHP phiên bản 5.4.0, Trait được định nghĩa là một cơ chế cho phép lập trình viên tận dụng khả năng tái sử dụng lại code (code reusability) khi lập trình với ngôn ngữ chỉ cho phép thừa kế từ một class duy nhất (hay còn gọi là single inheritance).

Có thể hiểu một cách đơn giản, Trait như một class, là nơi tập hợp một nhóm phương thức mà chúng ta muốn sử dụng lại trong class khác.

Quay lại một chút về khái niệm kế thừa thì hẳn là chúng ta đều hình dung được rằng việc sử dụng tính chất này có thể tái sử dụng được mã nguồn ở nhiều nơi trong dự án mà không bị lặp code. Ví dụ mình có một ClassA với phương thức methodA(), một ClassB với phương thức methodB(), bây giờ yêu cầu là thiết kế một ClassC có thể sử dụng cả 2 methodA() lẫn methodB() ở 2 class trên. Đối với ngôn ngữ PHP, chúng ta có một giải pháp là cho ClassB kế thừa ClassA (lúc này ClassB có thể sửa dụng các phương thức của ClassA), sau đó cho ClassC kế thừa ClassB (ClassC mới vừa có thể dùng các phương thức của ClassA lẫn ClassB). Giải pháp này hoàn toàn OK cho đến khi dự án phình to ra khi có tới hàng trăm class, hàng nghìn method thì sao ? Tuy nhiên, trong dự án mình làm thì chỉ có tới vài chục class cần kế thừa từ một class thôi nhưng cũng đủ để đau đầu khi ứng dụng giải pháp trên. Lúc này, cần đưa ra một giải pháp tốt hơn và mình nghĩ đến việc sử dụng Trait.

Để hiểu hiểu rõ hơn về Trait thì chúng ta cùng đi vào phân tích bài toán nhỏ trong dự án của mình nhé !

  1. Ví dụ thực tế

Bài toán đặt ra là làm thế nào có thể kế thừa được chức năng tìm kiếm theo điều kiện ở tất cả các màn hình quản lý ? Đây chỉ là một bài toán nhỏ trong vô số bài toán ứng dụng của Trait thôi nha mọi người. Mình chỉ đưa ra để mọi người hình dung được việc khi cần sử dụng lại mã nguồn ở nhiều nơi thì nên nghĩ ngay đến việc sử dụng Trait.

Cú pháp khai báo Trait:

trait TraitName
{
    // code
}

trong đó TraitName chính là tên trait mà các bạn đặt, mình đã thay bằng một tên khác để xác định được nó đại diện cho chức năng tìm kiếm.

Tiếp theo là để sử dụng Trait trong một class, ta làm như sau:

class ClassName
{
    use TraitName; // goi den trait

    // code 
}

Trong đó:

  • ClassName: là tên của class mà bạn sử dụng traits.
  • TraitName: là tên của trait mà bạn muốn dùng.

Ví dụ như trong dự án của mình thì ClassName chính là tất cả những class mà mình muốn kế thừa ở trait, còn TraitName chính là tên trait ban đầu mình đặt đại diện cho chức năng tìm kiếm. Khi cần sử dụng trait thì mình chỉ cần use tên trait là xong :D

Như vậy là mình đã giải quyết được bài toán trong dự án của mình rồi nè :'>

Rất đơn giản phải không nào ?

Tuy nhiên, lúc đầu có bạn nào không biết phân biệt Trait với Abstract class và Interface class giống mình không nhỉ ? Vậy thì Trait có gì khác so với Abstract Class Interface Class ?

  • Đối với Abstract Class

Khác với Abstract Class, Trait hoạt động không dựa trên sự kế thừa. Nghe khá mâu thuẫn so với định nghĩa của Trait nhỉ ? Tuy nhiên thực tế là Trait làm việc giống như là ta copy lại những đoạn code có cùng một cách xử lý vào trong những class cần dùng vậy.  Còn việc sử dụng Abstract Class khiến ta phải xây dựng một mô hình phức tạp, từ class con gọi sang class cha, rồi từ các class khác muốn kế thừa class cha đó lại phải gọi thông qua từng class con của class cha.

  • Đối với Interface Class

Có thể nói về cơ bản thì Trait và Interface khá giống nhau về tính chất sử dụng. Cả hai đều không thể sử dụng nếu không có một class được implement cụ thể. Tuy nhiên chúng cũng có những sự khác nhau hết sức rõ rệt.

Interface giống như như một bản "hợp đồng" (nếu sử dụng) chỉ ra rằng: "đối tượng có thể làm việc này", do vậy ta phải implement nó thì mới sử dụng được. Trong khi đó Trait chỉ nói : "đối tượng có khả năng làm việc này".

Thực tế là không phải bao giờ ta có thời gian để implement lại tất cả method có trong interface nếu chúng xử lý giống nhau phải không? Khi có nhiều chức năng xử lý giống nhau, và được ứng dụng trong nhiều class khác nhau (có thể khác nhau về bản chất) thì chúng ta nên sử dụng traits để giúp code ngắn gọn (một phần vì PHP không hỗ trợ đa kế thừa).

Bài viết của mình đến đây là hết nha mọi người ! Hằn là bây giờ mọi người đều có cái nhìn tổng quan hơn về Trait, biết Trait là gì, dùng khi nào, ứng dụng ra sao ? Mong là sau bài viết này, khi các bạn gặp phải bài toán cần sử dụng lại mã nguồn ở nhiều nơi thì nghĩ ngay đến Trait nha!

Ngoài ra Trait có rất nhiều ứng dụng trong các bài toán như Model, hay SoftDelete trong Laravel,... Dưới đây là tài liệu tham khảo để mọi người có thêm hiểu biết về Trait!

Tài liệu tham khảo:

  1. https://culttt.com/2014/06/25/php-traits/
  2. https://www.sitepoint.com/php-traits-good-or-bad/
  3. https://www.develodesign.co.uk/news/laravel-traits-what-are-traits-and-how-to-create-a-laravel-trait/
  4. https://www.sitepoint.com/using-traits-in-php-5-4/
  5. https://viblo.asia/p/traits-va-cach-su-dung-traits-trong-php-63vKjvDyK2R


 

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.