Quali problemi dovrò affrontare se rimuovo il concetto di interfacce dal mio codice?

3

Ho programmato per molti anni, ma non mi sento ancora a mio agio con il concetto di "Interfacce". Provo ad usare le interfacce ma molte volte non vedo un uso obbligatorio per questo. Penso che questo sia probabilmente perché i progetti non erano così grandi, o perché le interfacce sono più utili quando è coinvolto il lavoro di squadra.

Quindi, facendo un altro tentativo di comprendere l'importanza delle interfacce, ecco la mia domanda:

Quali problemi dovrei affrontare se rimuovo il concetto di interfacce dal mio codice?

    
posta Vishwas G 15.12.2012 - 16:17
fonte

7 risposte

0

Risposta breve: Come risultato, le tue lezioni avranno un numero di punti malsano elevato e un conteggio dei metodi molto alto. (guarda le prime librerie UI, sono caricate con classi con, diciamo 100 metodi - brutto)

Le interfacce sono belle classi compatte (di solito 0-5 metodi) puramente astratte . Sono facili da ereditare da qualsiasi classe in qualsiasi luogo, in gerarchie di qualsiasi dimensione. L'unico scopo delle interfacce è l'ereditarietà , ma senza parti malvagie di ereditarietà multipla. Le interfacce portano naturalmente una buona compressione a pile di classi molto gonfie.

La rimozione delle interfacce non consente l'ereditarietà multipla da classi puramente astratte. Le classi rimanenti, invece di essere un alveare poco profondo e compatto multistrato, formeranno una foresta di alberi di eredità. Nel tempo, con i refactoring, la foresta tenderà a collassare su un singolo super albero con la classe radice "Object". L'albero non sarà più carino. Avrà una radice lunga con molte classi astratte non necessarie vicine alla radice e alla corona larga di classi foglia quasi identiche, dove è difficile distinguere una classe da un'altra. Le funzioni semplici introdotte nelle classi radice avranno troppo impatto su tutto e renderanno il codice non-mantenibile, non modificabile, superfunzionale e inutile. Ogni trattamento speciale di metodi comuni richiederà trucchi di specializzazione, come "reintrodurre".

Ci sarà una visibilità non necessaria per metodi eccessivamente pubblici. Molti metodi morti, che sono necessari per stare vicino alle classi radice, ma non hanno senso per la maggior parte delle classi foglia. Ad un certo punto la pila raggiungerà le dimensioni, la complessità e la quantità di conflitti interni, quando non sarà più possibile apportare cambiamenti, aggiustamenti o miglioramenti senza rompere qualcosa da qualche parte.

    
risposta data 16.12.2012 - 06:38
fonte
5

Rimuovendo le interfacce dal codice, indipendentemente dal fatto che siano state eseguite esplicitamente (ad es., il costrutto interface di Java) o implicitamente (ad es. una classe astratta C ++), rimuovi la tua capacità di sfruttare alcune forme di ereditarietà. (Dico "alcune forme" perché in termini generali, le interfacce sono equivalenti alle classi astratte ma sono trattate in modo diverso da lingue diverse.)

Prendi questo esempio, scritto in nessuna lingua particolare:

class Juice { ... };  // Represents any kind of juice.

interface Fruit {
    Boolean is_edible();
    Juice   make_juice();
};

class Apple implements Fruit {
    Boolean is_edible() { return true; }
    Juice make_juice() { ... ; return juice; }
}

class Banana implements Fruit {
    Boolean is_edible() { return true; }
    Juice make_juice() { ... ; return juice; }
}


// Processes and accumulates the juices of many fruits.
class Juicer {
    Vector<Juice> accumulated_juice;  // Where all the juice goes

    // Process a Fruit for consumption.  Complain if it's
    // not an edible fruit.
    Void process(Fruit f) {
      if ( ! f.is_edible() ) throw_exception;
      accumulated_juice.add(f.make_juice());
    }
}

Questo è abbastanza semplice. Il Juicer può process() qualsiasi Fruit lo passi perché l'interfaccia garantisce che:

  • Esiste un metodo is_edible() per determinare se Fruit è edibile
  • Esiste un metodo make_juice() per produrre il succo

Puoi sviluppare Kumquat o Mango o StarFruit e non dovrai mai modificare Juicer per comprenderli. Il succo è succo di frutta.

Se rimuovi le interfacce, tutta questa semplicità scompare:

// Assume same class declarations as above without "implements xxx" clause.

class Juicer {
    Vector<Juice> accumulated_juice;  // Where all the juice goes

    // Process a Fruit for consumption.  Complain if it's
    // not an edible fruit.
    Void process_apple(Apple a) {
        if ( ! a.is_edible() ) throw_exception;
        accumulated_juice.add(a.make_juice());
    }
    Void process_banana(Banana b) {
        if ( ! b.is_edible() ) throw_exception;
        accumulated_juice.add(b.make_juice());
    }
    // Add methods for additional fruits here.
}

Tutti i metodi process_xxx() sono identici tranne per il tipo che prendono come argomento. Anche se Apple e Banana hanno metodi chiamati is_edible() e make_juice() con firme identiche, la lingua non ti consentirà solo di chiamarli per nome perché fanno parte di classi diverse. Ciò significa che il tuo Juicer deve avere più codice aggiunto ogni volta che sviluppi un nuovo frutto.

Una volta che questo accadrà a più di queste poche classi, diventa un incubo di manutenzione se è necessario apportare modifiche al modo in cui il metodo process() funziona internamente. Inoltre, qualsiasi altro codice che gestisce i tuoi frutti deve avere tutto questo codice aggiuntivo

HTH.

    
risposta data 15.12.2012 - 17:30
fonte
3

Ero nella stessa barca con te per i primi due anni della mia carriera di programmatore. Sono riuscito ad andare avanti senza creare le mie interfacce. Non è possibile rimuovere completamente il concetto, tuttavia, poiché l'utilizzo della maggior parte delle librerie implicherà l'utilizzo di interfacce.

Consiglio vivamente di dedicare del tempo a ricercare cosa sono e alcuni casi di utilizzo di base. Ti mancheranno un sacco di strumenti potenti come l'iniezione di dipendenza se non impari come usarli. Inoltre, renderà il codice più disaccoppiato e più semplice da modificare se ci si basa su interfacce anziché tipi concreti.

    
risposta data 15.12.2012 - 16:48
fonte
3

What problems will I face if I remove the concept of interfaces from my code?

Quello che otterrai è il accoppiamento stretto del tuo sistema. Come dice il proverbio "Alla ricerca della qualità del codice: attenti alla coppia stretta!"

Che cos'è un sistema software accoppiato?

Probabilmente avete almeno sentito il termine accoppiamento usato nel contesto della programmazione orientata agli oggetti. Accoppiamento fa riferimento alle interrelazioni di componenti (o oggetti) in un'applicazione. Un'applicazione accoppiata liberamente è più modulare di quella strettamente accoppiata. Le sue componenti si basano su interfacce e classi astratte rispetto a quelle concrete, come farebbero in un sistema strettamente accoppiato. In un sistema liberamente accoppiato, i componenti sono correlati usando astrazioni invece di implementazioni.

Uno schema comune di sistema software strettamente accoppiato (GUI e DB senza livello di astrazione in mezzo)

LadipendenzadellaGUIdaun'implementazionepiuttostocheun'astrazionelimitailsistema.NonèpossibileutilizzarelaGUIsenzaavereildatabaseattivoefunzionante.Questodesignnonsembracosìmaledaunpuntodivistafunzionale-abbiamoscrittoappcomequestaperanniegliaereinonstannocadendodalcielo,dopotutto-matestarloèun'altrastoria.

OK,comesiaccoppiastretto?-Leinterfaccefarannoiltrucco?-Disabilital'accoppiamento!

La GUI nel sistema software sopra si basa su un'astrazione: un oggetto di accesso ai dati o DAO . L'implementazione del DAO dipende direttamente dal database, ma la GUI stessa non è impigliata. L'aggiunta di un'astrazione sotto forma di DAO disaccoppia l'implementazione del database dall'implementazione della GUI. Al posto del database, un'interfaccia è ora accoppiata al codice della GUI.

In conclusione - una cosa su cui gli sviluppatori sono giunti a concordare è che il codice ben scritto è gestibile e il principio di inversione delle dipendenze è un modo sicuro di progettare per la manutenibilità. L'inversione delle dipendenze sottolinea la dipendenza dalle astrazioni rispetto alle implementazioni , il che crea una grande quantità di flessibilità all'interno di una base di codice. L'applicazione di questa tecnica con l'aiuto di un DAO garantisce che non solo sarai in grado di modificare il tuo codice base quando ne hai bisogno, ma lo faranno anche altri sviluppatori.

    
risposta data 15.12.2012 - 23:11
fonte
2

Se non vedi l'esigenza di interfacce, potrebbero non essere necessarie. Direi, non creare un'interfaccia finché non hai una buona ragione per farlo. (Questo va sotto il principio che ogni carattere del tuo codice ha uno scopo.)

Ho lavorato molto nel mondo Java. Le interfacce sono dappertutto. Ci sono diversi motivi per queste interfacce incluso

  • Alcune librerie richiedono interfacce, ad es. se si desidera creare un proxy dinamico, è necessario utilizzare le interfacce per definire i tipi
  • Nascondere l'implementazione: questo è il classico motivo
  • "Perché potremmo averne bisogno in seguito" - non necessario e forse uno spreco di tempo per lo sviluppatore
  • "Perché qualcuno mi ha detto che dovrei creare interfacce" - non una buona ragione
risposta data 16.12.2012 - 07:44
fonte
1

Un'interfaccia è molto utile per nascondere i dettagli di implementazione. Per esempio. potrebbe esserci una classe di registrazione. Non è necessario sapere come viene gestita la registrazione, i messaggi di log possono essere salvati in un file di testo o in un database. Tutte le implementazioni di registrazione potrebbero implementare questa interaccia:

public interface Logging {
void log(String message, int level)
}

Se si utilizza l'interfaccia (e si ottiene l'implementazione tramite un'iniezione fabric o dependency) non si dipende da una determinata implementazione, che potrebbe cambiare nel tempo. E tu sei disponibile per cambiare l'implementazione senza alcuna modifica del codice.

    
risposta data 15.12.2012 - 16:24
fonte
1

Se non capisci quali interfacce offrono allora non capisci le interfacce. Quindi probabilmente non perderai molto se smetti di usarli perché è evidente che non li usi al massimo potenziale.

Le tre cose principali che mi mancherebbero sarebbero

  1. ereditarietà multipla. Se eredito da una classe base in un framework per ottenere benefici idraulici, come fa la classe a dire al mondo che può fare x? Interfacce.
  2. In relazione a # 1, posso definire il comportamento dell'applicazione in un componente che non ha alcuna dipendenza da alcunché. Puoi farlo con classi astratte fino a un certo punto, ma puoi incappare in problemi molto velocemente se lavori con terze parti, altre divisioni o anche con codice precedente.
  3. Queste due cose rendono molto più semplice testare nuove classi che si basano su interfacce.

Se non capisci questi tre vantaggi (e ce ne sono altri), allora lavori con un linguaggio che consente l'ereditarietà multipla vera e accetta i problemi che si presentano, usa linguaggi di scripting che sono molto più flessibili o semplicemente non capire quali interfacce rappresentano.

    
risposta data 15.12.2012 - 17:20
fonte

Leggi altre domande sui tag