Làm thế nào để ghi đè chức năng tính trạng và gọi nó từ chức năng ghi đè?


370

Kịch bản:

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 hoạt động và tôi không thể tìm ra cách gọi hàm đặc điểm giống như nó được kế thừa. 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ó hoạt động hay tôi phải ghi đè hoàn toàn chức năng đặc điểm phức tạp hơn nhiều so với điều này :)

Câu trả lời:


641

Người cuối cùng của bạn đã ở gần đó:

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 điểm không phải là một lớp học. 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, nó chỉ là sao chép và dán tự động ...


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

2
@PhillipWhelan sẽ rất tuyệt nếu bạn có thể thêm thông tin về những gì "không hoạt động như mong đợi". Viết như thế nó không giúp ích gì nhiều trong việc hiểu loại hành vi sai nào được mong đợi, và không đảm bảo với chúng tôi rằng đây không phải là một lỗi tạm thời của bạn. Có lẽ có một số câu hỏi SO về vấn đề bạn đang nói về? (Cuối cùng) Cảm ơn.
Kamafeather

1
Vấn đề là tất cả các phương pháp khác trong đặc điểm không còn được bao gồm.
malhal

2
Chỉ để tham khảo: Nếu chức năng đặc điểm của bạn là tĩnh, bạn có thể truy cập chức năng bằng cách gọi A::calc(1)
velop

4
Như Phillip đã đề cập (tôi nghĩ), làm thế nào bạn sẽ làm điều này cho một phương pháp của một đặc điểm trong khi vẫn bao gồm tất cả các phương pháp khác có cùng đặc điểm như bình thường? Tốt hơn là không tham khảo rõ ràng từng phương pháp.
Gannet

14

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

Bởi vì các lớp con không thực hiện phương thức trực tiếp, trước tiên chúng sẽ sử dụng phương thức đó nếu có cách khác sử dụng phương thức của lớp cha.

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ẽ ở đó), vd

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 các cách để ghi đè, nhưng vẫn truy cập phương thức tính trạng 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ể thấy nó hoạt động tại http://sandbox.onlinephpfifts.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5


8

Một cách tiếp cận khác nếu quan tâm - với một lớp trung gian bổ sung để sử dụng cách thức thông thường của OOO. Điều này giúp đơ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);
    }
}

6
Cách tiếp cận này sẽ cạo bất kỳ lợi thế nào bạn có bằng cách sử dụng traits. Giống như kết hợp nhiều tính trạng trong nhiều lớp (ví dụ: tính trạng A, B trong một lớp, tính trạng B, C, D trong lớp khác, tính trạng A, C trong lớp khác, v.v.)
Ionuț Stomsu

3
Không, sử dụng phương pháp này bạn vẫn có lợi thế là 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. Đặc điểm sẽ là vô dụng, nếu nó chỉ được sử dụng trong IntClass. Trong trường hợp đó, tốt hơn là đặt phương thức calc () trực tiếp vào lớp đó.
marcini

Điều này hoàn toàn không làm việc cho tôi. ScreenablePerson::save()tồn tại, Candidatesử dụng Validatingđặc điểm và mở rộng ScreenablePerson, và cả ba lớp đều có save().
Theodore R. Smith

1

Sử dụng một đặ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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.