+3

PHP Traits

Một vấn đề thường xuyên nảy sinh trong quá trình chúng ta muốn sử dụng kế thừa (extends) trong PHP thì chúng ta chỉ có thể kế thưa duy nhất từ một class cha mà thôi. Đây cũng là yếu điểm lớn nhất của extends, bởi vì, đôi khi việc kế thừa từ nhiều class khác nhau đem đến rất nhiều lời ích. Chúng ta có thể sử dụng nhiều methods của các class khác nhau, để tránh lặp lại code.

Ví dụ, chúng ta có class A với method X, class B với method Y. Bài toán đặt ra là, bây giờ tôi có một class C, với method Z. Làm cách nào để trong method Z tôi có thể sử dụng được cả method X (class A), mehod Y (class B) ?

Nếu bạn là một lập trình viên PHP thì chắc hản bạn cũng đã nắm được là PHP không hỗ trợ đa kế thừa. Một suy nghĩ đơn giản nhất mà cũng tự nhiên nhất nảy sinh trong đầu chúng ta là. Class C extens class B (=> method Z sử dụng được method Y), và class B extends class A (=> method Z sử dụng được method X).

Trong những bài toán nhỏ và vừa, những class A, B, C ở ví dụ trên còn đơn giản và không phức tạp. Nếu chúng ta gặp bài toán lớn hơn, phức tạp hơn, thì việc kế thừa như giải pháp ở bên trên thì quả thật là chúng ta đã tự bắn vào chân mình.

Để khắc phục được sự bất tiện này, trong bản PHP 5.4 đã đưa ra một khái niệm hoàn toàn mới so với những phiên bản PHP trước đây. Đó là Traits. Vậy Traits là gì? Bài này tôi xin giới thiệu đôi chút về Traits.

Định nghĩa

Traits có thể hiểu như là một class, nó tập hợp một nhóm các phương thức (method) mà chúng ta muốn sử dụng trong các class khác. Cũng giống như Abstract Class chúng ta không thể khởi tạo một đối tượng mới từ Traits.

Định nghĩa về Traits mọi người tự tìm hiểu thêm nhé. ^^. Sau đây tôi xin giới thiệu một số cách sử dụng Traits

Example

Khởi tạo một Traits có dạng.

<?php

trait Demo {
  public function demo()
  {
    return 'Demo Traits';
  }
}

Hãy tạo ra một vài class khác để sử dụng Traits mà chúng ta vừa tạo ra.


class Post {
  use Demo;
}

class Comment {
  use Demo;
}

Bạn có thể hình dung rằng, việc sử dụng Traits chính là chúng ta viết 1 phương thức để cả 2 class Post và Comment có thể sử dụng được. Sau khi khai báo như trên, để sử dụng được method demo trong Traits, chúng ta sử dụng đơn giản như sau:

$post = new Post;
echo $post->demo(); // 'Demo Traits'

$comment = new Comment;
echo $comment->demo(); // 'Demo Traits'

Qua ví dụ trên, chúng ta đã hiểu đôi chút cách sử dụng của Traits. Hãy xem ví dụ sau.

Multiple Traits

Hãy theo dõi đoạn code.

<?php
trait Hello
{
    function sayHello() {
        echo "Hello";
    }
}

trait World
{
    function sayWorld() {
        echo "World";
    }
}

class MyWorld
{
    use Hello, World;
}

$world = new MyWorld();
echo $world->sayHello() . " " . $world->sayWorld(); //Hello World

ở đây chúng ta có 2 Traits, Hello và World. Traits Hello, chỉ có thể in ra "Hello", còn Traits World in ra "World". Trong class MyWord chúng ta đã use cả 2 Traits trên, do đó để in ra được đoạn text "Hello World" thì chúng ta chỉ cẩn gọi phương thức bên trong mỗi Traits như trên.

Traits Composed of Traits

PHP 5.4 cho phép chúng ta tạo một Traits mới được cấu tạo từ nhiều Traits khác nhau. Nhờ đó, chúng ta chỉ cần include một Traits thay vì include nhiều Traits như trước đây. Xem ví dụ sau:


<?php
trait HelloWorld
{
    use Hello, World;
}

class MyWorld
{
    use HelloWorld;
}

$world = new MyWorld();
echo $world->sayHello() . " " . $world->sayWorld(); //Hello World

Trong ví dụ bên trên để sử dụng được Traits Hello và Traits World, chúng ta cần use Hello, World. Thay vì làm điều này, chúng ta khai báo một Traits mới với tên HelloWorld. Trong traits này chúng ta gọi đến 2 Trails con là Hello và World đã định nghĩa ở bên trên. Tất nhiên đẻ gọi được phương thức con của chúng, chúng ta vẫn gọi như cũ. ^^

** Ưu tiên theo thứ tự**

Các thứ tự ưu tiên sẽ là:

  • Method của những traits override đươc kế thừa các phương thức của lớp cha.
  • Method được định nghĩa tại class overide.
<?php
trait Hello
{
    function sayHello() {
        return "Hello";
    }

    function sayWorld() {
        return "Trait World";
    }

    function sayHelloWorld() {
        echo $this->sayHello() . " " . $this->sayWorld();
    }

    function sayBaseWorld() {
        echo $this->sayHello() . " " . parent::sayWorld();
    }
}

class Base
{
    function sayWorld(){
        return "Base World";
    }
}

class HelloWorld extends Base
{
    use Hello;
    function sayWorld() {
        return "World";
    }
}

$h =  new HelloWorld();
$h->sayHelloWorld(); // Hello World
$h->sayBaseWorld(); // Hello Base World

Conflict Resolution and Aliasing

Khi chúng ta sử dụng multiple traits, chúng ta khó tránh được những trường hợp như ví dụ sau.

<?php
trait Game
{
    function play() {
        echo "Playing a game";
    }
}

trait Music
{
    function play() {
        echo "Playing music";
    }
}

class Player
{
    use Game, Music;
}

$player = new Player();
$player->play();

Nếu chúng ta cứ gọi hàm đơn thuần như những ví dụ trước. Thì tôi muốn hỏi các bạn là, Output trong trường hợp này là gì? 😄. Hãy bắt tay vào code vào tìm câu trả lời nhé. hé hé. Bài toàn là tôi có một class Player nhưng muốn in ra cả 2 trường hợp là chơi game và nghe nhạc thì tôi phải làm thế nào????

Giải pháp là đây:

<?php
class Player
{
    use Game, Music {
        Game::play as gamePlay;
        Music::play insteadof Game;
    }
}

$player = new Player();
$player->play(); //Playing music
$player->gamePlay(); //Playing a game

Trên đây là giới thiệu một số cách sử dụng Traits trong PHP. Các bạn cùng tìm hiểu thêm về Traits nhé. ^^


All Rights Reserved

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