Design Patterns - Perché la necessità di interfacce?

3

OK. Sto imparando i modelli di design. Ogni volta che vedo qualcuno codice un esempio di un modello di progettazione usano le interfacce. Ecco un esempio:

link

Qualcuno può spiegarmi perché sono state necessarie le interfacce in questo esempio per dimostrare il modello di facciata? Il programma funziona se passi le classi alla facciata invece dell'interfaccia. Se non ho interfacce vuol dire

    
posta Kyle Johnson 07.11.2013 - 21:10
fonte

6 risposte

2

Le interfacce non sono correlate ai modelli di progettazione. Riguardano l'orientamento all'oggetto. In OO, il tipo di un oggetto è definito dal suo comportamento, cioè dal protocollo che parla (i suoi metodi).

Classi in linguaggi come Java e C #, tuttavia, definiscono anche la rappresentazione (campi) oltre al comportamento (metodi). Uno dei principi fondamentali di OO è che gli oggetti non possono conoscere la rappresentazione di altri oggetti anche del tipo stesso . Tuttavia, se si usano classi come tipi, altri oggetti della stessa classe possono conoscere la rappresentazione di altri oggetti, anche i campi private . Pertanto, utilizzando le classi come tipi si interrompe OO. (Sfortunatamente, sia in Java che in C #, le classi sono tipi.)

L'unico modo per fare programmazione orientata agli oggetti in Java o C # è usare le classi solo come fabbriche (cioè solo direttamente dopo new ) e mai come tipi (cioè mai come tipo di campo, tipo di ritorno o tipo di parametro di un metodo o argomento di tipo ad un tipo generico, mai cast ad una classe, non usarlo mai con instanceof ). E come tipi, usa le interfacce solo , non le classi (e certamente non le primitive in Java).

Questo è ciò che fanno le interfacce: abilitano la programmazione orientata agli oggetti in Java e C #, senza di essi, OOP sarebbe semplicemente impossibile in quelle lingue.

    
risposta data 08.11.2013 - 00:18
fonte
11

Pensa a un'interfaccia come un contratto. È un modo per dire: "Queste classi dovrebbero seguire queste regole".

Usiamo un esempio e chiamiamolo IAnimal . Potremmo dire che l'interfaccia definisce due metodi Run e Walk . È un modo per dire: "DEVO essere in grado di chiamare Run, Walk, ecc. Sulle classi che implementano IAnimal."

Perché è utile? Potresti voler creare una funzione che si basa sul fatto che devi essere in grado di chiamare Esegui e cammina, ad esempio, sull'oggetto. Si potrebbe avere il seguente:

public void RunThenWalk(Monkey m) {
    m.Run();
    m.Walk();
}

public void RunThenWalk(Dog d) {
    d.Run();
    d.Walk();
}

... e ripetilo per tutti gli oggetti che sai correre e camminare. Tuttavia, con la tua interfaccia IAnimal, puoi definire la funzione una volta come segue:

public void RunThenWalk(IAnimal a) {
    a.Run();
    a.Walk();
}

Programmando contro l'interfaccia, ti stai essenzialmente fidando delle classi per implementare l'intento dell'interfaccia. Quindi nel nostro esempio, il pensiero è "Non mi interessa come loro corrono e camminano, fintanto che corrono e camminano. Il mio RunThenWalk sarà valido fintanto che adempierà a quell'accordo. perfettamente bene senza sapere altro sulla classe. "

    
risposta data 08.11.2013 - 00:36
fonte
2

Puoi pensare allo scopo principale delle interfacce come consentire al codice di essere astratto piuttosto che specifico. In una lingua con sicurezza di tipo strong, ti consente di trattare più classi con somiglianze uguali senza dover sapere quale di questi tipi è in uso.

Ad esempio, potresti avere un'interfaccia per Persone in cui si estendono diverse classi figlie. Si potrebbe quindi avere un metodo che si occupa di persone, accettando qualsiasi o tutti i tipi di classi che implementano le persone senza alcuna preoccupazione per quello che sono oltre. Ciò è fornito anche da altre forme di ereditarietà, ma le interfacce hanno la speciale restrizione che loro stessi non possono avere un'implementazione.

    
risposta data 07.11.2013 - 21:30
fonte
0

Cerco di spiegarlo in termini di articolo a cui ti sei collegato: l'uso delle interfacce consente di creare test unitari per VehicleFacade , per testare quella classe in isolamento. Questo può essere ottenuto fornendo implementazioni fittizie di IEngineController , ITransmissionController , e così via, invece di usare il% reale di% co_de,% di% di co_de ecc.

Naturalmente, queste interfacce consentono anche di creare veicoli diversi da diversi controller del motore, diversi controller di trasmissione, ecc. e combinarli liberamente. In scenari del mondo reale, tuttavia, mi aspetto che il primo caso d'uso (test) si verifichi molto più spesso del secondo.

    
risposta data 07.11.2013 - 22:49
fonte
0

Penso che Larsenal abbia dato la risposta migliore finora, ma mi piacerebbe estenderlo a dare un esempio concreto dal funzionamento del codice a livello aziendale (non in C #, ma in un altro linguaggio basato su ECMAScript). Nel mio codice, posso trattare Classi che sono cose fondamentalmente diverse come se fossero le stesse dal punto di vista del codice client.

Nella mia applicazione, ho un'interfaccia IPaging che definisce funzioni come nextPage (), previousPage (), ecc. Questa interfaccia è implementata da View Classes, ma anche da Classi di controller. Ciò che questo mi permette di fare è avviare l'applicazione con una vista che implementa direttamente IPaging che viene passato alla variabile currentPaging dell'applicazione. Questa vista è già attiva e funzionante mentre i controllori sono caricati con i dati necessari per procedere. Questo rende subito qualcosa di interattivo di fronte agli utenti.

Una volta che hanno superato quella vista, la variabile currentPaging dell'applicazione riceve quindi un'istanza di un controller che implementa IPaging. Quel Controller potrebbe quindi implementare l'IPaging chiamando le stesse funzioni su una Vista di IPaging (che potrebbe avere di nuovo viste di IPaging al suo interno che consentono la navigazione gerarchica nidificata), o potrebbe implementare IPaging in un modo diverso, scambiando pagine di dati con una Vista che non ha nulla a che fare con IPaging. Le scelte sono letteralmente illimitate e posso utilizzare lo stesso framework applicativo con Views o Controllers che sono personalizzati per essere leggermente o molto diversi in base ai requisiti che otteniamo. E i nostri clienti, come la maggior parte dei clienti, sono pazzi, quindi dobbiamo essere il più flessibili possibile.

Non mi sono mai pentito di usare un'interfaccia quando forse ci sono solo una o due implementazioni, ma mi sono pentito di non aver usato un'interfaccia quando ho bisogno di un'implementazione che non erediti dalla stessa classe base e non l'ho usata un'interfaccia. E una volta che i riferimenti a una classe specifica vengono diffusi attraverso la base di codice e fatti riferimento a più progetti, è difficile rimettere il genio nella bottiglia.

Un vantaggio delle interfacce che ottengo nella lingua che uso (che sospetto potessi avere anche in C #) è che ho la possibilità di caricare le implementazioni delle interfacce che utilizzo in runtime da una fonte esterna. L'applicazione di caricamento non ha bisogno di compilare le definizioni di classe delle implementazioni caricate - ha solo bisogno della definizione dell'interfaccia per essere in grado di trasmettere a tale interfaccia. Le interfacce, per definizione, non contengono praticamente alcun codice.

    
risposta data 08.11.2013 - 04:15
fonte
0

L'articolo che hai citato è un terribile esempio per ottenere un apprezzamento per ciò che rende le interfacce utili. Mi ci è voluto davvero molto tempo prima di avere finalmente quel momento "aha" su ciò che rende le interfacce utili. Il motivo è che pensavo alle interfacce come classi base glorificate il cui nome è distorto per sembrare orientato all'azione, proprio come l'articolo che hai citato. Non c'è da meravigliarsi se ho avuto questa percezione errata perché la maggior parte degli articoli che parlano di interfacce fanno la stessa cosa.

Il problema che avevo con le interfacce di "apprezzamento" era che sembravano sempre trasformarsi in astratte classi base perché aveva più senso. L'alternativa era dichiarare l'interfaccia, dichiarare una classe derivata (base) dell'interfaccia che implementava molti dei metodi comuni che tutte le classi derivate concrete condividevano. Non aveva senso avere 2 classi per la maggior parte delle interfacce solo per fornire l'impianto idraulico quando si farebbe 1. Quindi l'interfaccia è semplicemente passata ad essere una classe base astratta.

Il mio momento "aha" è accaduto quando ho capito che anche se il nome dell'interfaccia era "action-oriented", in realtà era solo una classe base con un nome diverso. Pertanto, IEngineController non è realmente un'interfaccia, è una classe EngineControllerBase. Quello che ho capito è che le interfacce sono più utili quando non sono legate all'implementazione della classe derivata, ma descrivono invece azioni o capacità distinte in se stesse.

Quindi, IEngineController sarebbe meglio (e fornirebbe un articolo molto più illustrativo) se fosse chiamato IStartStoppable OPPURE avesse 2 interfacce separate. IStartable, IStoppable. Almeno per me, una volta superate le interfacce definenti come palchi di classi base astratte glorificate, il valore delle interfacce inizia a splendere.

    
risposta data 08.11.2013 - 16:37
fonte

Leggi altre domande sui tag