2013-02-21 5 views
15

Considerate le seguenti interfacce PHP:È possibile sovrascrivere i metodi di interfaccia con firme diverse, ma "compatibili"?

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 
} 

sto usando PhpStorm, e quando faccio questo, ottengo un errore nell'IDE che indica sostanzialmente la definizione per add() in SuperCollection non è compatibile con la definizione di cui l'interfaccia si estende, Collection.

In un modo, posso vedere che questo è un problema, poiché la firma del metodo non corrisponde a quella "sostituisce" esattamente. Tuttavia, ritengo che ciò sarebbe compatibile, poiché SuperItem estende Item, quindi vedrei lo add(SuperItem) lo stesso di add(Item).

Sono curioso di sapere se questo è supportato da PHP (versione 5.4 o successiva) e forse l'IDE ha un bug che non lo rileva correttamente.

+1

In 5.3, ottengo "Errore fatale: Dichiarazione di SuperCollection :: add() deve essere compatibile con quella della Collezione :: aggiungere ()". Quindi sembra che PhpStorm sia corretto. Perché pensi che 5.4 potrebbe comportarsi in modo diverso? – halfer

risposta

10

No, sono abbastanza sicuro che PHP non supporti questo, in nessuna versione, e preferirebbe sconfiggere il punto di un'interfaccia.

Il punto di un'interfaccia è che ti dà un contratto fisso con altro codice che fa riferimento alla stessa interfaccia.

Ad esempio, si consideri una funzione come questa:

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

Questa funzione si aspetta di ricevere un oggetto che implementa l'interfaccia Collection.

All'interno della funzione, il programmatore sarebbe in grado di scrivere chiamate a metodi definiti in Collection, sapendo che l'oggetto avrebbe implementato tali metodi.

Se si dispone di un'interfaccia sostituita come questa, si ha un problema con questo, perché un oggetto SuperCollection può essere passato alla funzione. Funzionerebbe perché è anche un oggetto Collection a causa dell'ereditarietà. Ma allora il codice nella funzione non potrebbe più essere sicuro di sapere quale sia la definizione del metodo add().

Un'interfaccia è, per definizione, un contratto fisso. È immutabile.

In alternativa, è possibile considerare l'utilizzo di classi astratte anziché di interfacce. Ciò ti consentirebbe di eseguire l'override in modalità non rigorosa, sebbene continuerai a ricevere errori se utilizzi la modalità rigorosa, per gli stessi motivi.

+0

Grazie per la spiegazione! Stavo pensando che questo dovrebbe funzionare nella mia testa, ma mi hai aiutato a vedere l'errore in questo. – jzimmerman2011

+5

Non sono d'accordo con questa risposta. Le interfacce non riguardano le definizioni del metodo o le relazioni in esso contenute (ad esempio, 'add()' l'uso di un elemento significa che è possibile anche 'delete()' it). La loro unica responsabilità è l'applicazione di firme dei metodi compatibili (input/output). Hai ragione quando si tratta di contratti fissi, ma errati in merito allo scopo del contratto. Due classi potrebbero facilmente implementare la stessa interfaccia e avere * semantica completamente diversa (ad esempio 'add()' potrebbe effettivamente * cancellare * elementi in una classe e non sarebbe importante finché gli input/output rimangono dello stesso tipo) . – FtDRbwLXw6

+1

@drrcknlsn, anche se non credo che invalida la spiegazione, è molto importante da ricordare. – qrazi

2

Il problema non è nell'IDE. In PHP non puoi sovrascrivere un metodo. E la compatibilità è solo nella direzione opposta: puoi tranquillamente aspettarti un'istanza della classe genitore e ricevere una sottoclasse. Ma quando ti aspetti una sottoclasse, non puoi essere sicuro se ricevi la classe genitore - la sottoclasse può definire metodi che non esistono nel genitore. Tuttavia, non è possibile sovrascrivere il metodo

+8

Per favore, per favore prova a usare le parole giuste. È possibile sovrascrivere un metodo in PHP, ma non sovraccaricarlo. –

0

Quando ho un metodo che potrebbe essere necessario sovraccaricare (quale PHP non supporta), mi assicuro che uno degli argomenti del metodo (di solito l'ultimo) sia un array. In questo modo posso passare tutto ciò di cui ho bisogno.Posso quindi testare all'interno della funzione i vari elementi dell'array per dirmi quale routine nel metodo che devo eseguire, di solito in un select/case.

2

Come soluzione temporanea sto utilizzando i blocchi PHPDoc nelle interfacce.

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 
} 

In questo modo, nel caso in cui si sta correttamente utilizzando interfacce, IDE dovrebbe aiutare a catturare alcuni errori. È possibile utilizzare similar technique per sovrascrivere anche i tipi di valore restituito.

0

L'estensione dell'interfaccia non è consentita per modificare le definizioni dei metodi. Se il tuo SuperItem sta estendendo Item, dovrebbe passare attraverso le classi che implementano l'interfaccia Collection senza problemi.

Ma in base a ciò che si vuole veramente fare, si può provare:

  • Creare un'interfaccia con metodi leggermente diversi per SuperItem e attuare tale:

    interface SuperCollection extends Collection { 
        public function addSuper(SuperItem $superItem); 
    } 
    
  • Usa sequenza decorator per creare quasi la stessa interfaccia senza estensione:

    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 
    } 
    

    Il n decorator (astratto) di classe, che utilizzerà questa interfaccia:

    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); 
        } 
    }