2012-08-13 38 views
249

Kịch bản:Làm thế nào để ghi đè lên hàm trait và gọi nó từ hàm bị ghi đè?

trait A { 
    function calc($v) { 
     return $v+1; 
    } 
} 

class MyClass { 
    use A; 

    function calc($v) { 
     $v++; 
     return A::calc($v); 
    } 
} 

print (new MyClass())->calc(2); // should print 4 

Mã này không làm việc, và tôi không thể tìm thấy một cách để gọi một chức năng đặc điểm như nó đã được thừa hưởng. Tôi đã thử gọi self::calc($v), static::calc($v), parent::calc($v), A::calc($v) và sau đây:

trait A { 
    function calc($v) { 
     return $v+1; 
    } 
} 

class MyClass { 
    use A { 
     calc as traitcalc; 
    } 

    function calc($v) { 
     $v++; 
     return traitcalc($v); 
    } 
} 

Không có gì hoạt động.

Có cách nào để làm cho nó làm việc hay tôi phải ghi đè lên hoàn toàn các chức năng đặc điểm đó là phức tạp hơn nhiều so với :)

Trả lời

431

người cuối cùng của bạn này là gần như có:

trait A { 
    function calc($v) { 
     return $v+1; 
    } 
} 

class MyClass { 
    use A { 
     calc as protected traitcalc; 
    } 

    function calc($v) { 
     $v++; 
     return $this->traitcalc($v); 
    } 
} 

Các đặc điểm là không phải một lớp. Bạn không thể truy cập trực tiếp các thành viên của nó. Về cơ bản, chỉ cần sao chép và dán tự động ...

+2

Perfect! Cảm ơn bạn! Rõ ràng bộ não của tôi đã tan chảy. :) – Shu

+8

chỉ để làm rõ - một khi lớp học của bạn xác định cùng một phương pháp, nó sẽ tự động ghi đè đặc điểm của nó. Đặc điểm đầy trong phương thức như @ircmaxell đề cập khi nó trống. – Yehosef

+0

Vì những lý do chưa biết, điều này hoàn toàn làm say đắm một đặc điểm mà tôi đã áp dụng điều này. Các phương pháp khác mà đặc tính đang kéo vào có thể không hoạt động như mong đợi khi bạn làm điều này. –

7

Nếu lớp thực hiện phương thức trực tiếp, lớp sẽ không sử dụng phiên bản đặc điểm. Có lẽ những gì bạn đang nghĩ đến là:

trait A { 
    function calc($v) { 
     return $v+1; 
    } 
} 

class MyClass { 
    function calc($v) { 
     return $v+2; 
    } 
} 

class MyChildClass extends MyClass{ 
} 

class MyTraitChildClass extends MyClass{ 
    use A; 
} 

print (new MyChildClass())->calc(2); // will print 4 

print (new MyTraitChildClass())->calc(2); // will print 3 

Vì lớp con không thực hiện phương pháp trực tiếp, trước tiên chúng sẽ sử dụng đặc điểm này nếu có sử dụng lớp đó.

Nếu bạn muốn, đặc điểm có thể sử dụng phương thức trong lớp cha (giả sử bạn biết phương thức sẽ có ở đó), ví dụ:

trait A { 
    function calc($v) { 
     return parent::calc($v*3); 
    } 
} 
// .... other code from above 
print (new MyTraitChildClass())->calc(2); // will print 8 (2*3 + 2) 

Bạn cũng có thể cung cấp cho cách để ghi đè lên, nhưng vẫn truy cập vào các phương pháp đặc điểm như sau:

trait A { 
    function trait_calc($v) { 
     return $v*3; 
    } 
} 

class MyClass { 
    function calc($v) { 
     return $v+2; 
    } 
} 


class MyTraitChildClass extends MyClass{ 
    use A { 
     A::trait_calc as calc; 
    } 
} 


class MySecondTraitChildClass extends MyClass{ 
    use A { 
     A::trait_calc as calc; 
    } 

    public function calc($v) { 
     return $this->trait_calc($v)+.5; 
    } 
} 


print (new MyTraitChildClass())->calc(2); // will print 6 
echo "\n"; 
print (new MySecondTraitChildClass())->calc(2); // will print 6.5 

Bạn có thể nhìn thấy nó làm việc tại http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5

4

Một cách tiếp cận thay thế nếu quan tâm - với một lớp trung gian phụ để sử dụng cách OOO bình thường. Này đơn giản hóa việc sử dụng với parent :: methodname

trait A { 
    function calc($v) { 
     return $v+1; 
    } 
} 

// an intermediate class that just uses the trait 
class IntClass { 
    use A; 
} 

// an extended class from IntClass 
class MyClass extends IntClass { 
    function calc($v) { 
     $v++; 
     return parent::calc($v); 
    } 
} 
+5

Cách tiếp cận này sẽ loại bỏ bất kỳ lợi thế nào bạn có bằng cách sử dụng 'trait's. Giống như kết hợp nhiều đặc điểm trong nhiều lớp (ví dụ, đặc điểm A, B trong một lớp, đặc điểm B, C, D trong một lớp khác, đặc điểm A, C trong một lớp khác vv) –

+3

Không, sử dụng cách tiếp cận này bạn vẫn có ưu điểm có một đặc điểm. Bạn có thể sử dụng đặc điểm này trong IntClass, nhưng bạn cũng có thể sử dụng nó trong nhiều lớp khác nếu bạn muốn. Trait sẽ vô dụng, nếu nó chỉ được sử dụng trong IntClass. Trong trường hợp đó, tốt hơn nên đặt phương thức calc() trực tiếp trong lớp đó. – marcini

0

Sử dụng đặc điểm khác:

trait ATrait { 
    function calc($v) { 
     return $v+1; 
    } 
} 

class A { 
    use ATrait; 
} 

trait BTrait { 
    function calc($v) { 
     $v++; 
     return parent::calc($v); 
    } 
} 

class B extends A { 
    use BTrait; 
} 

print (new B())->calc(2); // should print 4 
Các vấn đề liên quan