Qual è il punto di interfaccia in PHP?


224

Le interfacce consentono di creare codice che definisce i metodi delle classi che lo implementano. Non è tuttavia possibile aggiungere alcun codice a tali metodi.

Le classi astratte ti consentono di fare la stessa cosa, insieme all'aggiunta di codice al metodo.

Ora, se riesci a raggiungere lo stesso obiettivo con classi astratte, perché abbiamo persino bisogno del concetto di interfacce?

Mi è stato detto che ha a che fare con la teoria OO dal C ++ a Java, che è alla base delle cose OO di PHP. Il concetto è utile in Java ma non in PHP? È solo un modo per evitare di avere dei segnaposto disseminati nella classe astratta? Mi sto perdendo qualcosa?


4
Devi leggere questo: stackoverflow.com/a/384067/14673
Luc M

abbastanza sicuro che sia un aiuto mentale e un aiuto di comunicazione. le interfacce fungono da eccellenti strumenti didattici per l'API, poiché raggruppano i servizi che l'API espone insieme in un modo astratto che può essere letto senza conoscere l'implementazione. Questo è costoso, ma come persone che sono a proprio agio con l'interfaccia, possono già utilizzare le funzioni direttamente senza bisogno di classi, salvando così un puntatore. senza interfacce definite dalla lingua, può essere complicato pianificare in anticipo per molti programmatori, poiché le persone abituate a "dominare le lingue" spesso preferiscono progettare con interfacce piuttosto che sulla carta.
Dmitry,

Risposte:


142

L'intero punto delle interfacce è darti la flessibilità di costringere la tua classe a implementare interfacce multiple, ma non consentire comunque l'ereditarietà multipla. I problemi con l'ereditarietà da più classi sono molti e vari e il pagina di Wikipedia su di essi li riassume abbastanza bene.

Le interfacce sono un compromesso. La maggior parte dei problemi con ereditarietà multipla non si applica alle classi di base astratte, quindi la maggior parte dei linguaggi moderni oggigiorno disabilita l'ereditarietà multipla ma chiama interfacce di classi di base astratte e consente a una classe di "implementare" quanti ne desidera.


39
Si può anche dire che * Le interfacce forniscono il design per una classe con zero implementazione. * Le classi astratte forniscono un po 'di design, con qualche implementazione. Le classi astratte sono molto utili quando le classi secondarie condividono alcune somiglianze di implementazione, ma differiscono in alcune implementazioni.
Jrgns,

@Craig, non ci sono problemi intrinseci con l'ereditarietà multipla, solo che le lingue attuali non sono abbastanza potenti da implementarle correttamente. I "problemi" possono effettivamente essere risolti piuttosto facilmente, ad esempio affermando il percorso esplicito dell'ereditarietà per le funzioni ereditate con lo stesso nome può risolvere il dilemma del diamante.
Pacerier

15
Questo non è affatto ciò che riguarda le interfacce. Non è un compromesso sull'eredità multipla, si tratta di creare un contratto concettuale e astratto per gli oggetti da implementare e per altri oggetti / metodi da consumare. le interfacce sono uno strumento per il polimorfismo e non per l'ereditarietà diretta.
Madara's Ghost

123

Il concetto è utile nella programmazione orientata agli oggetti. Per me penso a un'interfaccia come a un contratto. Così a lungo la mia classe e la tua classe concordano su questo contratto di firma del metodo che possiamo "interfacciare". Per quanto riguarda le classi astratte quelle che vedo come più di classi base che eliminano alcuni metodi e ho bisogno di compilare i dettagli.


Questo mi ha fatto capire un po '. È come con gli spazi dei nomi, quindi il codice condiviso è più facile da usare e senza conflitti. È più facile quando due persone fanno lezioni sulla stessa base, giusto?
RedClover,

Non è necessario distinguere il concetto generale di "interfaccia" dalle interfacce concrete in un linguaggio come PHP? Qualsiasi funzione, ad esempio, ha una "interfaccia" che definisce come la usi e ne nasconde l'implementazione. Quindi quel tipo di interfaccia "contrattuale" non richiede una funzione linguistica speciale. Pertanto la funzione del linguaggio deve essere per qualcos'altro (o qualcosa in più).
UuDdLrLrSs

70

Perché avresti bisogno di un'interfaccia, se ci sono già classi astratte? Per impedire l'ereditarietà multipla (può causare più problemi noti).

Uno di questi problemi:

Il "problema del diamante" (a volte indicato come il "diamante mortale della morte") è un'ambiguità che sorge quando due classi B e C ereditano da A e la classe D eredita sia da B che da C. Se esiste un metodo in A che B e C hanno sovrascritto e D non ha la precedenza, quindi quale versione del metodo eredita D: quella di B o quella di C?

Fonte: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

Perché / quando utilizzare un'interfaccia? Un esempio ... Tutte le auto del mondo hanno la stessa interfaccia (metodi) ... AccelerationPedalIsOnTheRight(),BrakePedalISOnTheLeft() . Immagina che ogni marchio automobilistico avrebbe questi "metodi" diversi da un altro marchio. La BMW avrebbe i freni sul lato destro e la Honda avrebbe i freni sul lato sinistro della ruota. Le persone dovrebbero imparare come funzionano questi "metodi" ogni volta che comprano una marca diversa di auto. Ecco perché è una buona idea avere la stessa interfaccia in più "luoghi".

Cosa fa un'interfaccia per te (perché qualcuno dovrebbe persino usarne una)? Un'interfaccia ti impedisce di fare "errori" (ti assicura che tutte le classi che implementano un'interfaccia specifica, avranno tutti i metodi che sono nell'interfaccia).

// Methods inside this interface must be implemented in all classes which implement this interface.
interface IPersonService
{   
    public function Create($personObject);
}

class MySqlPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Create a new person in MySql database.
    }
}

class MongoPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Mongo database creates a new person differently then MySQL does. But the code outside of this method doesn't care how a person will be added to the database, all it has to know is that the method Create() has 1 parameter (the person object).
    }
}

In questo modo, il Create()metodo verrà sempre utilizzato allo stesso modo. Non importa se stiamo usando la MySqlPersonclasse o ilMongoPerson classe. Il modo in cui stiamo usando un metodo rimane lo stesso (l'interfaccia rimane la stessa).

Ad esempio, verrà utilizzato in questo modo (ovunque nel nostro codice):

new MySqlPerson()->Create($personObject);
new MongoPerson()->Create($personObject);

In questo modo, qualcosa del genere non può accadere:

new MySqlPerson()->Create($personObject)
new MongoPerson()->Create($personsName, $personsAge);

È molto più facile ricordare un'interfaccia e usare la stessa ovunque, piuttosto che più diverse.

In questo modo, l'interno del Create()metodo può essere diverso per le diverse classi, senza influire sul codice "esterno", che chiama questo metodo. Tutto il codice esterno che deve sapere è che il metodo Create()ha 1 parametro ( $personObject), perché è così che il codice esterno utilizzerà / chiamerà il metodo. Al codice esterno non importa cosa sta succedendo all'interno del metodo; deve solo sapere come usarlo / chiamarlo.

Puoi farlo anche senza un'interfaccia, ma se usi un'interfaccia, è "più sicuro" (perché ti impedisce di fare errori). L'interfaccia ti assicura che il metodo Create()avrà la stessa firma (stessi tipi e lo stesso numero di parametri) in tutte le classi che implementano l'interfaccia. In questo modo puoi essere sicuro che QUALSIASI classe che implementa l' IPersonServiceinterfaccia, avrà il metodo Create()(in questo esempio) e avrà bisogno solo di 1 parametro ( $personObject) per essere chiamato / usato.

Una classe che implementa un'interfaccia deve implementare tutti i metodi, che l'interfaccia ha / ha.

Spero di non ripetermi troppo.


Un'analogia davvero bella con i pedali dell'auto!
James,

24

La differenza tra l'uso di un'interfaccia e una classe astratta ha più a che fare con l'organizzazione del codice per me, che con l'applicazione del linguaggio stesso. Li uso molto quando preparo il codice con cui gli altri sviluppatori devono lavorare affinché rimangano all'interno dei modelli di progettazione previsti. Le interfacce sono una sorta di "progettazione per contratto" in base alla quale il tuo codice accetta di rispondere a una serie prescritta di chiamate API che potrebbero provenire da un codice a cui non hai l'asso.

Mentre l'ereditarietà da una classe astratta è una relazione "è una", ciò non è sempre ciò che si desidera e l'implementazione di un'interfaccia è più una relazione "si comporta come una". Questa differenza può essere abbastanza significativa in alcuni contesti.

Ad esempio, supponiamo che tu abbia un Account di classe astratto da cui si estendono molte altre classi (tipi di account e così via). Ha una serie particolare di metodi applicabili solo a quel gruppo di tipi. Tuttavia, alcune di queste sottoclassi di account implementano Versionable, Listable o Editable in modo che possano essere gettate nei controller che prevedono di utilizzare tali API. Il controller non si preoccupa del tipo di oggetto

Al contrario, posso anche creare un oggetto che non si estende da Account, ad esempio una classe astratta Utente, e ancora implementare Listable e Editable, ma non Versionable, che non ha senso qui.

In questo modo, sto dicendo che la sottoclasse FooUser NON è un account, ma si comporta come un oggetto modificabile. Allo stesso modo BarAccount si estende dall'account, ma non è una sottoclasse utente, ma implementa modificabile, elencabile e anche versione.

L'aggiunta di tutte queste API per Modificabile, Elencabile e Versatile nelle classi astratte stesse non solo sarebbe disordinata e brutta, ma duplicherebbe le interfacce comuni in Account e Utente, o forzerebbe il mio oggetto Utente a implementare Versione, probabilmente solo per lanciare un eccezione.


Questo è qui. Imponi rigorosamente agli sviluppatori di utilizzare i tuoi metodi senza estenderli o sovrascriverli
Nick,

21

Le interfacce sono essenzialmente un progetto per ciò che puoi creare. Definiscono quali metodi deve avere una classe , ma è possibile creare metodi extra al di fuori di tali limiti.

Non sono sicuro di cosa tu voglia dire non essere in grado di aggiungere codice ai metodi, perché puoi. Stai applicando l'interfaccia a una classe astratta o alla classe che la estende?

Un metodo nell'interfaccia applicato alla classe astratta dovrà essere implementato in quella classe astratta. Tuttavia, applica tale interfaccia alla classe di estensione e il metodo deve essere implementato solo nella classe di estensione. Potrei sbagliarmi qui: non uso le interfacce tutte le volte che potrei / dovrei.

Ho sempre pensato alle interfacce come un modello per sviluppatori esterni o un set di regole aggiuntivo per garantire che le cose fossero corrette.


9
In PHP, le interfacce contengono solo la dichiarazione del metodo e non l'implementazione effettiva. Le classi astratte, tuttavia, consentono di "aggiungere codice" ai metodi che devono essere ereditati dalle classi che lo estendono. Credo che questa differenza sia ciò a cui si riferiva mk.
nocash,

13

Utilizzerai le interfacce in PHP:

  1. Per nascondere l'implementazione: stabilisci un protocollo di accesso a una classe di oggetti e modifica l'implementazione sottostante senza refactoring in tutti i luoghi in cui hai usato quegli oggetti
  2. Per controllare il tipo, come per assicurarsi che un parametro abbia un tipo specifico $object instanceof MyInterface
  3. Per imporre il controllo dei parametri in fase di esecuzione
  4. Per implementare comportamenti multipli in una singola classe (costruire tipi complessi)

    class Car implementa EngineInterface, BodyInterface, SteeringInterface {

in modo che un Caroggetto possa ora start(), stop()(EngineInterface) o goRight(), goLeft()(Steering interface)

e altre cose a cui non riesco a pensare in questo momento

Il numero 4 è probabilmente il caso d'uso più ovvio che non puoi affrontare con classi astratte.

Dal pensiero in Java:

Un'interfaccia dice: "Ecco come appariranno tutte le classi che implementano questa particolare interfaccia". Pertanto, qualsiasi codice che utilizza una particolare interfaccia sa quali metodi possono essere chiamati per quell'interfaccia, e questo è tutto. Quindi l'interfaccia viene utilizzata per stabilire un "protocollo" tra le classi.


10

Le interfacce non esistono come base su cui le classi possono estendersi ma come una mappa delle funzioni richieste.

Di seguito è riportato un esempio dell'uso di un'interfaccia in cui una classe astratta non rientra:
Diciamo che ho un'applicazione di calendario che consente agli utenti di importare i dati del calendario da fonti esterne. Scriverei classi per gestire l'importazione di ogni tipo di origine dati (ical, rss, atom, json) Ciascuna di queste classi implementerebbe un'interfaccia comune che assicurerebbe che tutti abbiano i metodi pubblici comuni necessari alla mia applicazione per ottenere i dati.

<?php

interface ImportableFeed 
{
    public function getEvents();
}

Quindi, quando un utente aggiunge un nuovo feed, posso identificare il tipo di feed che è e utilizzare la classe sviluppata per quel tipo per importare i dati. Ogni classe scritta per importare dati per un feed specifico avrebbe un codice completamente diverso, altrimenti potrebbero esserci pochissime somiglianze tra le classi al di fuori del fatto che sono necessarie per implementare l'interfaccia che consente alla mia applicazione di consumarli. Se dovessi usare una classe astratta, potrei facilmente ignorare il fatto che non ho ignorato il metodo getEvents () che avrebbe quindi rotto la mia applicazione in questa istanza mentre l'uso di un'interfaccia non avrebbe permesso alla mia app di funzionare se QUALUNQUE dei metodi definito nell'interfaccia non esiste nella classe che lo ha implementato. La mia app non deve preoccuparsi della classe utilizzata per ottenere dati da un feed,

Per fare un ulteriore passo avanti, l'interfaccia si rivela estremamente utile quando torno alla mia app di calendario con l'intento di aggiungere un altro tipo di feed. L'uso dell'interfaccia ImportableFeed significa che posso continuare ad aggiungere più classi che importano diversi tipi di feed semplicemente aggiungendo nuove classi che implementano questa interfaccia. Ciò mi consente di aggiungere tonnellate di funzionalità senza dover aggiungere inutilmente ingombro alla mia applicazione principale poiché la mia applicazione principale si basa solo sulla disponibilità dei metodi pubblici richiesti dall'interfaccia, purché le mie nuove classi di importazione di feed implementino l'interfaccia ImportableFeed, quindi I so che posso semplicemente lasciarlo cadere e continuare a muovermi.

Questo è solo un inizio molto semplice. Posso quindi creare un'altra interfaccia che tutte le mie classi di calendario possono essere richieste per implementare che offre più funzionalità specifiche per il tipo di feed che gestisce la classe. Un altro buon esempio potrebbe essere un metodo per verificare il tipo di feed, ecc.

Questo va oltre la domanda, ma da quando ho usato l'esempio sopra: le interfacce vengono con il loro insieme di problemi se usate in questo modo. Mi trovo a dover garantire l'output che viene restituito dai metodi implementati per abbinare l'interfaccia e per raggiungere questo scopo uso un IDE che legge i blocchi PHPDoc e aggiungo il tipo restituito come un suggerimento di tipo in un blocco PHPDoc dell'interfaccia che poi traduci nella classe concreta che la implementa. Le mie classi che consumano l'output di dati dalle classi che implementano questa interfaccia sapranno almeno che si aspetta un array restituito in questo esempio:

<?php
interface ImportableFeed 
{
    /**
     * @return array
     */
    public function getEvents();
}

Non c'è molto spazio in cui confrontare classi e interfacce astratte. Le interfacce sono semplicemente mappe che una volta implementate richiedono che la classe disponga di una serie di interfacce pubbliche.


Buona spiegazione :) Grazie!
Razvan.432,

Solo per aggiungere informazioni: nella classe astratta, se dichiari il metodo come astratto, si comporta come un'interfaccia, quindi non puoi ignorare il fatto che non hai ignorato getEvents (). L'app fallirebbe allo stesso modo dell'interfaccia.
Rikudou_Sennin,

8

Le interfacce non sono solo per assicurarsi che gli sviluppatori implementino determinati metodi. L'idea è che, poiché è garantito che queste classi abbiano determinati metodi, è possibile utilizzare questi metodi anche se non si conosce il tipo effettivo della classe. Esempio:

interface Readable {
  String read();
}

List<Readable> readables; // dunno what these actually are, but we know they have read();
for(Readable reader : readables)
  System.out.println(reader.read());

In molti casi, non ha senso fornire una classe base, astratta o no, perché le implementazioni variano in modo selvaggio e non condividono nulla in comune oltre ad alcuni metodi.

I linguaggi tipizzati dinamicamente hanno il concetto di "digitazione anatra" in cui non sono necessarie interfacce; sei libero di supporre che l'oggetto abbia il metodo che stai chiamando. Questo risolve il problema in linguaggi tipicamente statici in cui il tuo oggetto ha un metodo (nel mio esempio, read ()), ma non implementa l'interfaccia.


6

A mio avviso, le interfacce dovrebbero essere preferite rispetto alle classi astratte non funzionali. Non sarei sorpreso se ci fosse anche un colpo di performance lì, poiché c'è solo un oggetto istanziato, invece di analizzarne due, combinandoli (anche se, non posso essere sicuro, non ho familiarità con i meccanismi interni di OOP PHP).

È vero che le interfacce sono meno utili / significative rispetto a, diciamo, Java. D'altra parte, PHP6 introdurrà un suggerimento ancora maggiore, incluso il suggerimento per i valori di ritorno. Ciò dovrebbe aggiungere un valore alle interfacce PHP.

tl; dr: interfaces definisce un elenco di metodi che devono essere seguiti (think API), mentre una classe astratta fornisce alcune funzionalità di base / comuni, che le sottoclassi perfezionano per esigenze specifiche.


PHP 6 non verrà mai rilasciato. PHP 6 era un progetto in fase di sviluppo dal 2005-2010, ma è stato ritardato e alla fine cancellato. PHP 7 è la versione successiva, principalmente per evitare confusione con l'ex progetto PHP 6.
Ashu Jha,

5

Non ricordo se PHP è diverso sotto questo aspetto, ma in Java puoi implementare più interfacce, ma non puoi ereditare più classi astratte. Suppongo che PHP funzioni allo stesso modo.

In PHP puoi applicare più interfacce separandole con una virgola (penso, non trovo che sia una semplice assunzione).

Per quanto riguarda più classi astratte potresti avere più abstract che si estendono a vicenda (di nuovo, non ne sono totalmente sicuro, ma penso di averlo già visto da qualche parte). L'unica cosa che non puoi estendere è una classe finale.


4

Le interfacce non daranno al tuo codice alcun aumento delle prestazioni o qualcosa del genere, ma possono fare molto per renderlo mantenibile. È vero che una classe astratta (o anche una classe non astratta) può essere utilizzata per stabilire un'interfaccia per il tuo codice, ma interfacce appropriate (quelle che definisci con la parola chiave e che contengono solo le firme dei metodi) sono semplicemente più facili da ordina e leggi.

Detto questo, tendo a usare la discrezione quando decido se usare o meno un'interfaccia su una classe. A volte voglio implementazioni di metodi predefiniti o variabili che saranno comuni a tutte le sottoclassi.

Naturalmente, anche il punto sull'implementazione dell'interfaccia multipla è valido. Se si dispone di una classe che implementa più interfacce, è possibile utilizzare un oggetto di quella classe come tipi diversi nella stessa applicazione.

Il fatto che la tua domanda riguardi PHP, tuttavia, rende le cose un po 'più interessanti. Digitare le interfacce non è ancora incredibilmente necessario in PHP, dove puoi praticamente alimentare qualsiasi cosa con qualsiasi metodo, indipendentemente dal suo tipo. Puoi digitare staticamente i parametri del metodo, ma alcuni di questi sono rotti (String, credo, provoca alcuni singhiozzi). Abbinalo al fatto che non puoi digitare la maggior parte degli altri riferimenti e non c'è molto valore nel cercare di forzare la digitazione statica in PHP ( a questo punto ). E per questo motivo, il valore delle interfacce in PHP , a questo puntoè molto meno di quanto lo sia nelle lingue più tipizzate. Hanno il vantaggio della leggibilità, ma poco altro. L'implementazione multipla non è nemmeno vantaggiosa, perché devi ancora dichiarare i metodi e dare loro corpi all'interno dell'implementatore.


2

Di seguito sono riportati i punti per l'interfaccia PHP

  1. Viene utilizzato per definire il numero richiesto di metodi nella classe [se si desidera caricare html, sono richiesti id e nome, quindi in questo caso l'interfaccia include setID e setName].
  2. L'interfaccia forza forzatamente la classe per includere tutti i metodi definiti in essa.
  3. È possibile definire il metodo solo nell'interfaccia con l'accessibilità pubblica.
  4. Puoi anche estendere l'interfaccia come una classe. Puoi estendere l'interfaccia in php usando extends la parola chiave.
  5. Estendi interfaccia multipla.
  6. Non è possibile implementare 2 interfacce se entrambe condividono la funzione con lo stesso nome. Si genererà un errore.

Codice di esempio:

interface test{
    public function A($i);
    public function B($j = 20);
}

class xyz implements test{
    public function A($a){
        echo "CLASS A Value is ".$a;
    }
    public function B($b){
        echo "CLASS B Value is ".$b;
    }
}
$x = new xyz();
echo $x->A(11);
echo "<br/>";
echo $x->B(10);

2

Abbiamo visto che le classi e le interfacce astratte sono simili in quanto forniscono metodi astratti che devono essere implementati nelle classi secondarie. Tuttavia, hanno ancora le seguenti differenze:

1. Le interfacce possono includere metodi e costanti astratti, ma non possono contenere metodi e variabili concrete.

2.Tutti i metodi nell'interfaccia devono essere nell'ambito della visibilità pubblica .

3. Una classe può implementare più di un'interfaccia, mentre può ereditare da una sola classe astratta.

                                  interface                      abstract class
the code                     - abstract methods               - abstract methods
                             - constants                      - constants                  
                                                              - concrete methods
                                                              - concrete variables

access modifiers             
                             - public                         - public
                                                              - protected
                                                              - private
                                                                etc.
number of parents          The same class can implement
                           more than 1 interface              The child class can 
                                                              inherit only from 1 abstract class

Spero che questa volontà aiuti a chiunque a capire!


2

Le interfacce sono come i tuoi geni.

Le lezioni astratte sono come i tuoi veri genitori.

I loro scopi sono ereditari, ma nel caso di classi astratte vs interfacce, ciò che è ereditato è più specifico.


2

Non conosco altre lingue, qual è il concetto di interfaccia lì. Ma per PHP, farò del mio meglio per spiegarlo. Sii paziente e commenta se questo ha aiutato.

Un'interfaccia funziona come un "contratto", specificando cosa fa un insieme di sottoclassi, ma non come lo fanno.

La regola

  1. Un'interfaccia non può essere istanziata.

  2. Non è possibile implementare alcun metodo in un'interfaccia, ovvero contiene solo .signature del metodo ma non dettagli (body).

  3. Le interfacce possono contenere metodi e / o costanti, ma nessun attributo. Le costanti di interfaccia hanno le stesse restrizioni delle costanti di classe. I metodi di interfaccia sono implicitamente astratti.

  4. Le interfacce non devono dichiarare costruttori o distruttori, poiché si tratta di dettagli di implementazione a livello di classe.

  5. Tutti i metodi in un'interfaccia devono avere visibilità pubblica.

Ora facciamo un esempio. Supponiamo di avere due giocattoli: uno è un cane e l'altro è un gatto.

Come sappiamo, un cane abbaia e miagola. Questi due hanno lo stesso metodo di parlare, ma con funzionalità o implementazione diverse. Supponiamo che stiamo offrendo all'utente un telecomando con un pulsante speak.

Quando l'utente preme il pulsante di parlare, il giocattolo deve parlare, non importa se si tratta di un cane o un gatto.

Questo è un buon caso per usare un'interfaccia, non una classe astratta perché le implementazioni sono diverse. Perché? Ricorda

Se è necessario supportare le classi secondarie aggiungendo un metodo non astratto, è necessario utilizzare le classi astratte. Altrimenti, le interfacce sarebbero la tua scelta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.