2013-02-21 28 views
15

Hãy xem xét các giao diện PHP sau:Bạn có thể ghi đè các phương thức giao diện với chữ ký khác, nhưng "tương thích" không?

interface Item { 
    // some methods here 
} 

interface SuperItem extends Item { 
    // some extra methods here, not defined in Item 
} 

interface Collection { 
    public function add(Item $item); 
    // more methods here 
} 

interface SuperCollection extends Collection { 
    public function add(SuperItem $item); 
    // more methods here that "override" the Collection methods like "add()" does 
} 

Tôi đang sử dụng PHPStorm, và khi tôi làm điều này, tôi nhận được một lỗi trong IDE mà về cơ bản khẳng định các định nghĩa cho add() trong SuperCollection không tương thích với các định nghĩa trong giao diện nó mở rộng, Collection.

Trong một cách, tôi có thể thấy vấn đề này là một vấn đề, vì chữ ký của phương thức không khớp với phương thức này "ghi đè" chính xác. Tuy nhiên, tôi cảm thấy rằng điều này sẽ tương thích, vì SuperItem mở rộng Item, vì vậy tôi sẽ xem add(SuperItem) giống như add(Item).

Tôi tò mò muốn biết liệu điều này có được hỗ trợ bởi PHP (phiên bản 5.4 hoặc cao hơn) hay không, và có thể IDE có lỗi không đúng cách.

+1

Trong 5.3, tôi nhận được "Fatal error: Tuyên bố của SuperCollection :: add() phải phù hợp với điều đó của Bộ sưu tập :: thêm () ". Vì vậy, có vẻ như PhpStorm là chính xác. Tại sao bạn nghĩ rằng 5.4 có thể hành xử khác nhau? – halfer

Trả lời

10

Không, tôi chắc rằng PHP không hỗ trợ điều này, trong bất kỳ phiên bản nào và nó sẽ đánh bại điểm của giao diện.

Điểm của giao diện là nó cung cấp cho bạn một hợp đồng cố định với mã khác tham chiếu cùng một giao diện.

Ví dụ, hãy xem xét một hàm như thế này:

function doSomething(Collection $loopMe) { ..... } 

Chức năng này hy vọng sẽ nhận được một đối tượng mà thực hiện giao diện Collection.

Trong hàm, lập trình viên có thể ghi cuộc gọi đến các phương thức được xác định trong Collection, biết rằng đối tượng sẽ triển khai các phương thức đó.

Nếu bạn có giao diện bị ghi đè như thế này, thì bạn có vấn đề với điều này, vì đối tượng SuperCollection có thể được chuyển vào hàm. Nó sẽ hoạt động vì nó cũng là đối tượng Collection do thừa kế. Nhưng sau đó mã trong hàm không còn có thể chắc chắn rằng nó biết định nghĩa của phương thức add() là gì.

Giao diện theo định nghĩa là một hợp đồng cố định. Nó là bất biến.

Thay vào đó, bạn có thể xem xét sử dụng các lớp trừu tượng thay vì giao diện. Điều này sẽ cho phép bạn ghi đè ở chế độ không nghiêm ngặt, mặc dù bạn vẫn sẽ gặp lỗi nếu bạn sử dụng Chế độ nghiêm ngặt, vì những lý do tương tự.

+0

Cảm ơn bạn đã giải thích! Tôi đã nghĩ rằng điều này sẽ làm việc trong đầu của tôi, nhưng bạn đã giúp tôi nhìn thấy lỗi trong đó. – jzimmerman2011

+5

Tôi phải không đồng ý với câu trả lời này. Giao diện không quan tâm đến các định nghĩa phương thức hoặc các mối quan hệ trong đó (ví dụ: 'add()' ing một mục có nghĩa là bạn cũng có thể 'xóa()' nó). Trách nhiệm duy nhất của họ là thực thi các chữ ký phương thức tương thích (đầu vào/đầu ra). Bạn đúng về chúng là hợp đồng cố định, nhưng không chính xác về mục đích của hợp đồng đó. Hai lớp có thể dễ dàng thực hiện cùng một giao diện và có * ngữ nghĩa hoàn toàn khác nhau (ví dụ 'add()' có thể thực sự * xóa * các mục trong một lớp và nó sẽ không quan trọng miễn là các đầu vào/đầu ra vẫn cùng loại) . – FtDRbwLXw6

+1

@ drrcknlsn, tôi không nghĩ rằng nó làm mất hiệu lực giải thích, nó là rất nhiều điều cần lưu ý. – qrazi

2

Sự cố không nằm trong IDE. Trong PHP bạn không thể ghi đè lên một phương thức. Và khả năng tương thích chỉ theo hướng ngược lại - bạn có thể an toàn mong đợi một cá thể của lớp cha và nhận một lớp con. Nhưng khi bạn mong đợi một lớp con, bạn không thể an toàn nếu bạn nhận được lớp cha - lớp con có thể định nghĩa các phương thức không tồn tại trong phần tử cha. Tuy nhiên, bạn không thể ghi đè phương thức

+8

Vui lòng thử sử dụng đúng từ. Bạn có thể ghi đè lên một phương thức trong PHP, chỉ cần không quá tải nó. –

0

Khi tôi có một phương pháp mà tôi có thể cần phải quá tải (mà PHP không hỗ trợ), tôi đảm bảo rằng một trong các đối số phương pháp (thường là cuối cùng) là một mảng. Bằng cách này tôi có thể vượt qua bất cứ điều gì tôi cần.Sau đó tôi có thể thử nghiệm trong hàm cho các phần tử mảng khác nhau để cho tôi biết những gì thường trình trong phương thức tôi cần thực hiện, thường là trong một trường hợp chọn /.

2

Cách giải quyết khác là tôi đang sử dụng khối PHPDoc trong giao diện.

interface Collection { 
    /** 
    * @param Item $item 
    */ 
    public function add($item); 
    // more methods here 
} 

interface SuperCollection extends Collection { 
    /** 
    * @param SuperItem $item 
    */ 
    public function add($item); 
    // more methods here that "override" the Collection methods like "add()" does 
} 

Bằng cách này, trong trường hợp bạn sử dụng đúng giao diện, IDE sẽ giúp bạn nắm bắt một số lỗi. Bạn cũng có thể sử dụng similar technique để ghi đè các loại giá trị trả lại.

0

Giao diện mở rộng không được phép thay đổi định nghĩa phương thức. Nếu SuperItem của bạn đang mở rộng Item, nó sẽ đi qua các lớp thực hiện giao diện Collection mà không có vấn đề gì.

Nhưng dựa vào những gì bạn thực sự muốn làm, bạn có thể thử:

  • Tạo giao diện với các phương pháp hơi khác nhau cho SuperItem và thực hiện điều đó:

    interface SuperCollection extends Collection { 
        public function addSuper(SuperItem $superItem); 
    } 
    
  • Sử dụng Decorator Pattern để tạo ra giao diện gần như giống nhau mà không mở rộng:

    interface Collection { 
        public function add(Item $item); 
        // more methods here 
    } 
    
    interface SuperCollection { 
        public function add(SuperItem $item); 
        // more methods here that "override" the Collection methods like "add()" does 
    } 
    

    n trang trí (trừu tượng) lớp, mà sẽ sử dụng giao diện này:

    class BasicCollection implements Collection { 
        public function add(Item $item) 
        { 
        } 
    } 
    
    class DecoratingBasicCollection implements SuperCollection { 
        protected $collection; 
    
        public function __construct(Collection $collection) 
        { 
         $this->collection = $collection; 
        } 
    
        public function add(SuperItem $item) 
        { 
         $this->collection->add($item); 
        } 
    } 
    
Các vấn đề liên quan