Interfaccia o una classe astratta: quale usare?


327

Spiegare quando dovrei usare un PHP interfacee quando dovrei usare un abstract class?

Come posso cambiare il mio abstract classin un interface?

Risposte:


458

Utilizzare un'interfaccia quando si desidera forzare gli sviluppatori che lavorano nel proprio sistema (incluso dall'utente) per implementare un determinato numero di metodi sulle classi che costruiranno.

Utilizzare una classe astratta quando si desidera forzare gli sviluppatori che lavorano nel proprio sistema (incluso dall'utente) per implementare un determinato numero di metodi e dall'utente si desidera fornire alcuni metodi di base che li aiuteranno a sviluppare le proprie classi figlio.

Un'altra cosa da tenere a mente è che le classi client possono estendere solo una classe astratta, mentre possono implementare più interfacce. Quindi, se stai definendo i tuoi contratti comportamentali in classi astratte, ciò significa che ogni classe figlio può essere conforme solo a un singolo contratto. A volte è una buona cosa, quando vuoi forzare i tuoi programmatori di utenti lungo un determinato percorso. Altre volte sarebbe male. Immagina se le interfacce numerabili e Iterator di PHP fossero classi astratte anziché interfacce.

Un approccio che è comune quando non si è sicuri della strada da percorrere (come indicato dal cletus di seguito ) è quello di creare un'interfaccia e quindi la propria classe astratta implementare tale interfaccia.


12
Ho cercato tutto il giorno di capire gli usi abstracte le interfacelezioni, il tuo post ha chiarito tutto. Grazie mille Alan
afarazit,

4
Un altro vantaggio delle classi astratte è la capacità di definire metodi protetti astratti . Non sempre utile, ma può tornare utile in alcune architetture.
netcoder

Quindi in molti casi dovremmo usare la classe astratta a causa della flessibilità - questa è la mia conclusione :)
ymakux,

3
@volocuga: non necessariamente, come ha sottolineato Alan, solo un singolo abstract può essere esteso. Personalmente non mi piace l'abstract implementa un'idea di interfaccia perché contribuisce all'offuscamento del codice ed è meno diretta, IMO.
Prefisso

171

Le differenze tra an Abstract Classe an Interface:

Classi astratte

Una classe astratta può fornire alcune funzionalità e lasciare il resto per la classe derivata .

  • La classe derivata può o meno sovrascrivere le funzioni concrete definite nella classe base.

  • Una classe figlio estesa da una classe astratta dovrebbe essere logicamente correlata.

Interfaccia

Un'interfaccia non può contenere alcuna funzionalità . Contiene solo definizioni dei metodi.

  • La classe derivata DEVE fornire il codice per tutti i metodi definiti nell'interfaccia .

  • Classi completamente diverse e non correlate possono essere raggruppate logicamente insieme usando un'interfaccia.


1
Potete fornire un esempio di vita reale per dimostrarlo?
RN Kushwaha,

1
Qual è la differenza tra abstract class X implements Ye class X implements Y?
Webinan,

3
@Webinan abstract class X implements YDichiari che la funzionalità di massa di X deve essere implementata in una classe derivata e che sia la classe astratta che la derivata devono contenere funzioni definite in Y, mentre class X implements Yimplica solo che la classe X deve contenere funzioni definite in Y. Se la tua interfaccia Y non è progettato per essere implementato da nessuna classe diversa da XI, in realtà salta la definizione di Y come interfaccia e implementa le funzioni in Y come funzione astratta pubblica / protetta / privata per assicurarsi che siano implementate nella classe derivante.
Calle Bergström,

1
Le interfacce possono non solo contenere la definizione di metodi, ma possono anche contenere costanti
Thielicious

Mi è piaciuto il tuo confronto. Quindi volevo aggiungere qualcosa. Le interfacce possono avere costanti di classe predefinite mentre la classe astratta no.
Noman Ibrahim,

128

Perché usare le classi astratte? Il seguente è un semplice esempio. Diciamo che abbiamo il seguente codice:

<?php 

class Fruit {
    private $color;

    public function eat() {
        // chew
    }

    public function setColor($c) {
        $this->color = $c;
    }
}

class Apple extends Fruit {
    public function eat() {
        // chew until core
    }
}

class Orange extends Fruit {
    public function eat() {
        // peeling
        // chew
    }
}

Ora ti do una mela e la mangi. Che sapore ha? Ha il sapore di una mela.

<?php 
$apple = new Apple();
$apple->eat();

// Now I give you a fruit.
$fruit = new Fruit();
$fruit->eat();

Che sapore ha? Beh, non ha molto senso, quindi non dovresti essere in grado di farlo. Ciò si ottiene rendendo astratta la classe Fruit e il metodo eat al suo interno.

<?php 
abstract class Fruit {
    private $color;

    abstract public function eat(){}

    public function setColor($c) {
        $this->color = $c;
    }
}
?>

Una classe astratta è proprio come un'interfaccia, ma è possibile definire metodi in una classe astratta mentre in un'interfaccia sono tutti astratti. Le classi astratte possono avere metodi sia vuoti che lavorativi / concreti. Nelle interfacce, le funzioni definite non possono avere un corpo. In classi astratte, possono.

Un esempio del mondo reale:

<?php 
abstract class person {

    public $LastName;
    public $FirstName;
    public $BirthDate;

    abstract protected function write_info();
}

final class employee extends person{

    public $EmployeeNumber;
    public $DateHired;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to emloyee dbase table <br>";   
    }
}

final class student extends person{

    public $StudentNumber;
    public $CourseName;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to student dbase table <br>";
    }
}

///----------
$personA = new employee;
$personB = new student;

$personA->FirstName="Joe";
$personA->LastName="Sbody";

$personB->FirstName="Ben";
$personB->LastName="Dover";

$personA->write_info();
// Writing Sbody's info to emloyee dbase table
$personB->write_info();
// Writing Dover's info to student dbase table 

2
Ciao, questa risposta probabilmente ha ottenuto dei voti negativi a causa del modo in cui è formattata. Sarebbe bello se non fosse un grosso blocco di codice (quattro spazi trasformano qualcosa in un blocco di codice, indifferentemente il testo per estrarlo dal blocco) e se questo è stato incollato da qualche parte (sembra così) sarebbe educato riconoscerli.
Camilo Martin,

9
Ti amo per l'uomo esempio di frutta! Da quando ho iniziato a studiare php, questo esempio mi chiarisce molte grazie
Raheel,

23
+1 What does that taste like? Well, it doesn't make much sense, so you shouldn't be able to do that.Ora conosco l'abstract!
Webinan,

Cosa fa la finalparola chiave? Ottimo post, grazie.
Gus,

1
@VineeshKalarickal Quello che non capisco nell'esempio Person è la differenza tra: 1) usando la classe astratta Person (come nell'esempio); 2) scrivere Persona come classe standard e fare in modo che Employee e Student prevalgano sul metodo write_info ().
Ferex,

66

La migliore pratica è utilizzare un'interfaccia per specificare il contratto e una classe astratta come solo una sua implementazione. Quella classe astratta può riempire molto del bollettino in modo da poter creare un'implementazione semplicemente ignorando ciò che è necessario o che si desidera senza costringere l'utente a utilizzare un'implementazione particolare.


37

Solo per buttare questo nel mix, ma come menzionato da Cletus usando un'interfaccia in combinazione con una classe astratta, uso spesso l'interfaccia per chiarire il mio pensiero progettuale.

Per esempio:

<?php
class parser implements parserDecoratorPattern {
    //...
}

In questo modo, chiunque legga il mio codice (e sa chi è un modello di Decorator) saprà immediatamente a) come costruisco il mio parser eb) sarà in grado di vedere quali metodi vengono utilizzati per implementare il modello di decoratore.

Inoltre, e potrei essere fuori base qui non essendo un programmatore Java / C ++ / etc, ma i tipi di dati possono entrare in gioco qui. I tuoi oggetti sono di un tipo e quando li passi attorno al tipo conta a livello di programmazione. Lo spostamento degli elementi contraibili nell'interfaccia determina solo i tipi restituiti dai metodi, ma non il tipo base della classe che lo implementa.

È tardi e non riesco a pensare a un esempio di psudo-codice migliore, ma ecco qui:

<?php
interface TelevisionControls {};
class Remote implements TelevisionControls {};
class Spouse implements TelevisionControls {};
Spouse spouse = new Spouse();
Remote remote = new Remote();
isSameType = (bool)(remote == spouse)

1
Che esempio fantastico! ;)
Joel Murphy

@ matt2000 Non sessista E ora lavora anche per matrimoni omosessuali. Ottima modifica. :)
Austen Hoogen,

4
Questo è un esempio fantastico! Tuttavia, non penso che tu possa istanziare una classe astratta, ma piuttosto una classe che si estende da una classe astratta
Arielle Nguyen,

2
non ucciderebbe per rendere il codice sudo almeno simile alla lingua in questione. qualcuno dovrebbe mettere un po 'di dollari lì dentro.
ho lottato con un orso.

2
Mentre l'esempio è divertente, non possiamo istanziare direttamente una classe astratta, per favore apporta modifiche nell'esempio in modo che le persone non possano considerarlo un valido utilizzo in PHP.
saji89,

16

La differenza principale è che una classe astratta può contenere un'implementazione predefinita mentre un'interfaccia non può.

Un'interfaccia è un contratto di comportamento senza alcuna implementazione.


16

Inoltre, vorrei aggiungere qui che solo perché qualsiasi altro linguaggio OO ha qualche tipo di interfaccia e anche l'astrazione non significa che abbiano lo stesso significato e scopo di PHP. L'uso di astrazione / interfacce è leggermente diverso mentre le interfacce in PHP in realtà non hanno una funzione reale. Sono semplicemente usati per ragioni semantiche e legate allo schema. Il punto è avere un progetto il più flessibile possibile, espandibile e sicuro per future estensioni, indipendentemente dal fatto che lo sviluppatore abbia in seguito un piano di utilizzo totalmente diverso o meno.

Se il tuo inglese non è nativo, puoi cercare quali sono effettivamente le astrazioni e le interfacce. E cerca anche sinonimi.

E questo potrebbe aiutarti come metafora:

INTERFACCIA

Diciamo che hai cucinato un nuovo tipo di torta con le fragole e hai inventato una ricetta che descrive gli ingredienti e i passaggi. Solo tu sai perché sta assaggiando così bene e piace ai tuoi ospiti. Quindi decidi di pubblicare la tua ricetta in modo che anche altre persone possano provare quella torta.

Il punto qui è

- per fare la cosa giusta
- fare attenzione
- per prevenire cose che potrebbero andare male (come troppe fragole o qualcosa del genere)
- per renderlo facile per le persone che lo provano
- per dirti quanto tempo è cosa fare (come la messa in scena )
- per dire quali cose PUOI fare ma NON DEVI

Questo è esattamente ciò che descrive le interfacce. È una guida, un insieme di istruzioni che osservano il contenuto della ricetta. Come se volessi creare un progetto in PHP e desideri fornire il codice su GitHub o con i tuoi compagni o altro. Un'interfaccia è ciò che le persone possono fare e ciò che non dovresti. Regole che lo reggono: se disobbedisci a uno, l'intero costrutto verrà infranto.


ASTRAZIONE

Per continuare con questa metafora qui ... immagina, questa volta sei l'ospite a mangiare quella torta. Quindi stai provando quella torta usando la ricetta ora. Ma vuoi aggiungere nuovi ingredienti o cambiare / saltare i passaggi descritti nella ricetta. Quindi cosa viene dopo? Pianifica una versione diversa di quella torta. Questa volta con bacche nere e non bacche di paglia e più crema alla vaniglia ... buonissimo.

Questo è ciò che potresti considerare un'estensione della torta originale. Fondamentalmente ne fai un'astrazione creando una nuova ricetta perché è un po 'diversa. Ha alcuni nuovi passaggi e altri ingredienti. Tuttavia, la versione a bacca nera ha alcune parti che hai preso dall'originale: questi sono i passaggi di base che ogni tipo di torta deve avere. Come gli ingredienti come il latte - Questo è ciò che ogni classe derivata ha.

Ora vuoi scambiare ingredienti e passaggi e questi DEVONO essere definiti nella nuova versione di quella torta. Questi sono metodi astratti che devono essere definiti per la nuova torta, perché dovrebbe esserci un frutto nella torta, ma quale? Quindi prendi le bacche nere questa volta. Fatto.

Ecco fatto, hai esteso la torta, seguito l'interfaccia e estratto passaggi e ingredienti da essa.


1
Questo è stato il mio confronto preferito di tutto ciò che riguarda PHP. Aveva davvero senso. Grazie!
cbloss793,

13

Per aggiungere ad alcune delle risposte già eccellenti:

  • Le classi astratte ti consentono di fornire un certo grado di implementazione, le interfacce sono modelli puri. Un'interfaccia può solo definire la funzionalità , non può mai implementarla.

  • Qualsiasi classe che implementa l'interfaccia si impegna a implementare tutti i metodi che definisce o deve essere dichiarata astratta.

  • Le interfacce possono aiutare a gestire il fatto che, come Java, PHP non supporta l'ereditarietà multipla. Una classe PHP può estendere un solo genitore. Tuttavia, puoi promettere a una classe di implementare tutte le interfacce che desideri.

  • tipo: per ogni interfaccia implementata, la classe assume il tipo corrispondente. Poiché qualsiasi classe può implementare un'interfaccia (o più interfacce), le interfacce uniscono efficacemente tipi altrimenti non correlati.

  • una classe può sia estendere una superclasse sia implementare un numero qualsiasi di interfacce:

    class SubClass extends ParentClass implements Interface1, Interface2 {
        // ...
    }

Per favore, spiega quando dovrei usare un'interfaccia e quando dovrei usare la classe astratta?

Utilizzare un'interfaccia quando è necessario fornire solo un modello senza implementazione, e si desidera assicurarsi che qualsiasi classe che implementa tale interfaccia disponga degli stessi metodi di qualsiasi altra classe che la implementa (almeno).

Utilizzare una classe astratta quando si desidera creare una base per altri oggetti (una classe parzialmente costruita). La classe che estende la tua classe astratta utilizzerà alcune proprietà o metodi definiti / implementati:

<?php
// interface
class X implements Y { } // this is saying that "X" agrees to speak language "Y" with your code.

// abstract class
class X extends Y { } // this is saying that "X" is going to complete the partial class "Y".
?>

Come posso cambiare la mia classe astratta in un'interfaccia?

Ecco un caso / esempio semplificato. Elimina tutti i dettagli di implementazione. Ad esempio, cambia la tua classe astratta da:

abstract class ClassToBuildUpon {
    public function doSomething() {
          echo 'Did something.';
    }
}

per:

interface ClassToBuildUpon {
    public function doSomething();
}

12

Da un punto di vista filosofico:

  • Una classe astratta rappresenta una relazione "è un". Diciamo che ho frutti, beh, avrei una classe astratta Fruit che condivide responsabilità e comportamenti comuni.

  • Un'interfaccia rappresenta una relazione "da fare". Un'interfaccia, secondo me (che è l'opinione di uno sviluppatore junior), dovrebbe essere nominata da un'azione, o qualcosa di simile ad un'azione (mi dispiace, non riesco a trovare la parola, non sono un madrelingua inglese) diciamo IEatable. Sai che può essere mangiato, ma non sai cosa mangi.

Dal punto di vista della codifica:

  • Se i tuoi oggetti hanno un codice duplicato, significa che hanno un comportamento comune, il che significa che potresti aver bisogno di una classe astratta per riutilizzare il codice, cosa che non puoi fare con un'interfaccia.

  • Un'altra differenza è che un oggetto può implementare tutte le interfacce di cui hai bisogno, ma puoi avere solo una classe astratta a causa del "problema del diamante" (controlla qui per sapere perché! Http://it.wikipedia.org/wiki/ Multiple_inheritance # The_diamond_problem )

Probabilmente dimentico alcuni punti, ma spero che possa chiarire le cose.

PS: Il "è un" / "dovrebbe fare" è portato dalla risposta di Vivek Vermani, non intendevo rubare la sua risposta, solo per riutilizzare i termini perché mi piacevano!


2
Credo che la parola che stai cercando sia commestibile.
Travis Weston,

1
In realtà, credo che sia un "Verbo", una "parola che fa"
Grizly

7

Le differenze tecniche tra una classe astratta e un'interfaccia sono già elencate esattamente nelle altre risposte. Voglio aggiungere una spiegazione per scegliere tra una classe e un'interfaccia mentre scrivo il codice per motivi di programmazione orientata agli oggetti.

Una classe dovrebbe rappresentare un'entità mentre un'interfaccia dovrebbe rappresentare il comportamento.

Facciamo un esempio. Un monitor di computer è un'entità e dovrebbe essere rappresentato come una classe.

class Monitor{
    private int monitorNo;
}

È progettato per fornire un'interfaccia di visualizzazione all'utente, pertanto la funzionalità deve essere definita da un'interfaccia.

interface Display{
    void display();
}

Ci sono molte altre cose da considerare come spiegato nelle altre risposte, ma questa è la cosa più basilare che la maggior parte delle persone ignora durante la programmazione.


2
PHP non definisce i tipi di ritorno e l'OP ha taggato questa domanda conPHP
Purefan,

1

Volevo solo aggiungere un esempio di quando potrebbe essere necessario utilizzare entrambi. Attualmente sto scrivendo un gestore di file associato a un modello di database in una soluzione ERP generica.

  • Ho più classi astratte che gestiscono il greggio standard e anche alcune funzionalità speciali come la conversione e lo streaming per diverse categorie di file.
  • L'interfaccia di accesso ai file definisce un insieme comune di metodi necessari per ottenere, archiviare ed eliminare un file.

In questo modo, posso avere più modelli per file diversi e un insieme comune di metodi di interfaccia con chiara distinzione. L'interfaccia fornisce la corretta analogia con i metodi di accesso piuttosto che con una classe astratta di base.

Più in là, quando realizzerò adattatori per diversi servizi di archiviazione di file, questa implementazione consentirà di utilizzare l'interfaccia altrove in contesti totalmente diversi.

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.