2012-05-08 13 views
8

Stavo leggendo la documentazione di Dart ed ero un po 'confuso, forse perché vengo da Ruby, su come usare le interfacce. Ovviamente, le interfacce non sono uniche per Dart e ci sono un certo numero di spiegazioni là fuori quando si dovrebbe usare un'interfaccia. This one, ad esempio, sembra dire che le interfacce sono utili solo quando si è in una squadra. Cosa dovrebbe significare nel mondo dell'open source, in cui tutti leggono e riutilizzano il codice di qualcun altro?Quando utilizzare le interfacce in Dart?

Una interessante spiegazione che ho visto sembrava di essere il che implica che le interfacce sono utilizzati:

  1. in lingue che non hanno l'ereditarietà multipla, e
  2. per quella materia in qualche modo servire come una soluzione per l'assenza di eredità multipla.

Non lo capisco. Capisco che i moduli in Ruby sono una soluzione perché mi consentono di definire metodi reali con corpi reali. Le interfacce mi consentono solo di definire quali metodi dovrebbe avere una classe che la implementa. Qual è il trucco? Qualcuno può dire di un esempio veramente utile in cui posso immediatamente vedere il valore dell'uso delle interfacce?

P.S. Su una nota correlata, c'è un modo per utilizzare l'ereditarietà multipla in Dart?

risposta

19

Le interfacce sono utili perché consentono di cambiare le implementazioni di una classe, consentendo comunque la convalida che il tipo che viene passato soddisfa i requisiti dell'interfaccia.

adottare le seguenti (spesso usato) Esempio:

interface Quackable { 
    void quack(); 
} 

Questa definisce i requisiti di una classe che saranno passate a un metodo come:

sayQuack(Quackable quackable) { 
    quackable.quack(); 
} 

che consente di fare uso di qualsiasi implementazione di un oggetto Quackable, ad esempio:

class MockDuck implements Quackable { 
    void quack() => print("quack"); 
} 

class EnterpriseDuck implements Quackable { 
    void quack() { 
    // connect to three enterprise "ponds" 
    // and eat some server bread 
    // and say "quack" using an messaging system 
    } 

} 

Entrambe queste implementazioni funzioneranno con la funzione sayQuack(), ma una richiede un'infrastruttura significativamente inferiore rispetto alle altre.

sayQuack(new EnterpriseDuck()); 
sayQuack(new MockDuck()); 

Io uso questo schema tutto il tempo nel mondo Java, per la costruzione di soluzioni che fanno uso di qualche "anatra impresa". Quando si sviluppa localmente, tutto ciò di cui ho semplicemente bisogno è poter chiamare la funzione sayQuack() e restituire alcuni dati fittizi e fittizi.

duck typing

Perché Dart è eventualmente digitato, in realtà non hanno bisogno di utilizzare l'interfaccia, semplicemente scrivendo una classe che contiene la firma metodo corretto funziona (anche se gli strumenti non saranno in grado per convalidarlo).

class Person { // note: no implements keyword 
    void quack() => "I'm not a duck"; 
} 

sayQuack(new Person()); // provides the quack method, so this will still work 

Tutte le classi sono interfacce

Infine, tutte le classi sono anche le interfacce. Ciò significa che anche se un sistema di terze parti può essere stato scritto senza utilizzare le interfacce, è comunque possibile utilizzare una classe concreta come se fosse un'interfaccia.

Ad esempio, immaginiamo la seguente libreria impresa:

class EnterpriseDuck { // note: no implements keyword 
    void quack() { 
    // snip 
    } 
} 

sayQuack(EnterpriseDuck duck) { // takes an instance of the EnterpriseDuck class 
    duck.quack(); 
} 

e si desidera passare un anatra finto nel metodo sayQuack in modo che il tipo di controllo può convalidare. È possibile creare il mockDuck per implementare l'interfaccia implicita EnterpriseDuck, semplicemente utilizzando l'EnterpriseDuck come interfaccia:

class MockDuck implements EnterpriseDuck { 
    void quack() => "I'm a mock enterprise duck"; 
} 

ereditarietà multipla

In termini di ereditarietà multipla, questo non è possibile a Dart. È possibile, tuttavia, implementare interfacce multiple e fornire le proprie implementazioni dei metodi richiesti, ad esempio:

class MultiDuck implements Quackable, EnterpriseDuck, Swimable { 
    // snip... 
} 

interfacce possono avere classi di default

Come si utilizza Dart, troverete che la maggior parte delle classi" "sono in realtà interfacce. Elenco, stringa ecc. Sono tutte le interfacce con le implementazioni predefinite fornite. Quando si chiama

List myList = new List(); 

in realtà si sta utilizzando un'interfaccia List, e la nuova parola chiave reindirizza dall'interfaccia a un'implementazione lista predefinita sottostante.

Per quanto riguarda lo sviluppo in team

interfacce sono utili nel team di sviluppo, anche nel mondo open source. L'interfaccia definisce i metodi e le proprietà che dovresti costruire in modo che il tuo componente funzioni con il mio componente. È possibile creare la propria implementazione di test di tale interfaccia e io posso costruire la mia implementazione concreta di tale interfaccia e, quando avremo finito, possiamo integrarci. Senza l'interfaccia condivisa pubblicata, avrei bisogno di fornire la mia implementazione concreta prima che potessi davvero iniziare.

Spero che questo aiuti!

+2

Grazie davvero Chris, era esattamente ciò di cui avevo bisogno. Inoltre, i nomi delle classi negli esempi sono adorabili. – snitko

+0

nel pensare tu intendi, due dei tuoi commenti sul codice: "// nota: nessuna parola chiave dell'interfaccia" anziché "// nota: nessuna parola chiave implements". – GameAlchemist

+3

Considerare l'aggiornamento di questo o aggiungere un commento a questo post spiegando che la parola chiave dell'interfaccia non viene più utilizzata in Dart. –

1

Le interfacce fanno parte del sistema di tipi in Dart e le dichiarazioni di tipo sono facoltative. Ciò significa che anche le interfacce sono opzionali.

Le interfacce consentono di documentare i metodi a cui risponde l'oggetto. Se si implementa l'interfaccia, si promette di implementare tutti i metodi nell'interfaccia.

Dart utilizza queste informazioni per presentare avvisi in fase di compilazione in caso di mancata corrispondenza dei tipi, per fornire suggerimenti più utili per l'assistenza codice e per fornire assistenza con alcuni refactoring.

+0

Certo, capisco che sono opzionali. E non vedo il valore nel usarli. Gli avvisi del tempo di compilazione non sembrano essere una grande vittoria. Tuttavia, ammetto che potrei essere fondamentalmente sbagliato nella mia valutazione. Ecco perché stavo cercando un esempio reale che dimostri il valore dell'uso delle interfacce. – snitko

4

Fondamentalmente, le interfacce non hanno nulla a che fare con l'ereditarietà multipla. È possibile simulare più interfacce di ereditarietà e abuso quando lo si fa, ma se si desidera una vera ereditarietà multipla (o mixine o tratti), Dart non li fornisce (al momento - credo che i mixin troveranno la loro strada in qualche giorno).

Quali sono le interfacce valide per il contratto esplicito .Supponi di avere due componenti A e B che devono funzionare tra loro. Puoi sicuramente chiamare lo B da A direttamente e funzionerà, ma la prossima volta che vorrai cambiare B, dovrai guardare A come lo sta usando. Questo perché B non ha esposto un'interfaccia esplicita. Sì, la parola giusta per le interfacce non è implementa ma esporre.

Se si nasconde l'attuazione di B dietro un'interfaccia e solo prevedere che l'interfaccia a A, allora si può cambiare B a piacimento e solo preoccuparsi di ancora esponendo la stessa interfaccia. L'interfaccia può anche essere esposta da più di una classe e il chiamante non deve preoccuparsi (o nemmeno saperlo).

Si noti che l'interfaccia di parola ha due significati qui: il generale contratto del componente, che può anche essere descritto in parole povere nella documentazione, e un linguaggio speciale costruire che consente di descrivere (e anche applicando) alcune parti del contratto direttamente all'interno del linguaggio di programmazione.

Non è necessario utilizzare il costrutto del linguaggio, ma è considerato di buon stile utilizzarlo per descrivere le parti del contratto che il linguaggio di programmazione consente.

Ora, che diamine è un contratto qui? In parole povere, il contratto è una descrizione di ciò che il componente si aspetta dal suo utente e ciò che l'utente può aspettarsi dal componente.

Per esempio, diciamo che ho un metodo che calcola il valore assoluto di un numero:

class MathUtils { 
    /// Computes absolute value of given [number], which must be a [num]. 
    /// Return value is also a [num], which is never negative. 
    absoluteValue(number) { 
    ... here's the implementation ... 
    } 
} 

Il contratto qui è completamente descritto nel commento di documentazione (beh, non completamente, abbiamo potuto descrivere anche quale valore assoluto è, ma questo è abbastanza buono). Bene ... ma alcune parti del commento possono essere espresse direttamente nella lingua, giusto?

class MathUtils { 
    /// Computes absolute value of given [number]. 
    /// Return value is never negative. 
    num absoluteValue(num number) { 
    ... here's the implementation ... 
    } 
} 

nota che alcune parti del contratto semplicemente non possono essere espresse nel linguaggio di programmazione - qui, il linguaggio ha idea di cosa valore assoluto è, questo deve rimanere nel commento. Inoltre, non puoi esprimere che il valore di ritorno non è mai negativo, quindi anche questo deve rimanere nel commento. Ma in realtà, i lettori del codice sanno quello che un valore assoluto è (e che non è mai negativo) e il nome del metodo è piuttosto chiaro circa lo scopo, in modo che il commento può essere completamente lasciato fuori:

class MathUtils { 
    num absoluteValue(num number) { 
    ... here's the implementation ... 
    } 
} 

Così ora, alcune parti del contratto sono espresse esplicitamente, usando i mezzi del linguaggio, e alcune parti sono espresse implicitamente (ci si basa sulle persone che sanno quale sia il valore assoluto).

E le interfacce nella lingua vengono utilizzate per disaccoppiare il contratto dall'implementazione. Probabilmente è eccessivo usare nei programmi piccoli, ma si ripaga quando si eseguono programmi più grandi (che non devono coinvolgere il lavoro di squadra).

Uff, si è rivelato essere più lungo di quanto mi aspettassi. Spero possa aiutare.

Problemi correlati