Quali metodi dovrebbero essere messi in un'interfaccia e quali in classi astratte?

5

Ho visto molti framework e moduli e il loro standard che seguono è come questo

  1. UserInterface che hanno alcuni metodi predefiniti
  2. AbstractUserClass che implementa userInterface
  3. Quindi GenericUserClass che si estende da AbstractuserClass
  4. Quindi altre classi che si estendono da quella generica

Ora ho visto che la classe astratta ha funzioni addizionali all'interfaccia, e la classe generica ha anche funzioni aggiuntive.

  1. Quindi sono confuso quali metodi dovrebbero andare dove
  2. A volte vedo class A extends Abstractuserclass e talvolta class A extends Abstractuserclass implements UseraInterface . Qual è la differenza se Abstractclass già impelementi Userinterface
posta user26 02.01.2014 - 03:12
fonte

4 risposte

7

Una regola empirica è questa:

  • Le interfacce definiscono l'API pubblica (contratto) o una determinata funzionalità, se lo desideri,
  • Le classi astratte (maggio) forniscono un'API privata (per le classi estese da utilizzare), come la funzionalità condivisa.

So i am confused which methods should go where

Se un metodo deve far parte dell'API pubblica, aggiungilo all'interfaccia e aggiorna le dipendenze dove necessario (ad esempio, la classe Abstract e / o Generic).

Altrimenti, implementa il metodo in alto come è necessario in base a quanto generico o specifico.

Sometimes i see class A extends Abstractuserclass and sometimes class A extends Abstractuserclass implements useraInterface. what is the difference if Abstractclass already implements Userinterface

Non c'è differenza in quel caso; non è necessario specificare un'interfaccia due volte se l'antenato lo implementa già, perché l'interprete genera un errore se si tenta di interrompere il contratto.

Questo avrebbe senso solo se la classe figlio implementasse completamente un'altra interfaccia. E questa è una forza di interfacce; dal momento che stai definendo una funzionalità piuttosto che un'implementazione specifica, puoi fare in modo che qualsiasi classe implementa qualsiasi interfaccia, se lo desideri. Questo è di grande aiuto quando si scrivono classi di simulazione per i test.

    
risposta data 02.01.2014 - 03:21
fonte
3

L'interfaccia è un contratto - un'API che espone le funzionalità necessarie per l'oggetto che rappresenta. Pertanto, un'interfaccia dovrebbe supportare tutti i metodi correlati al concetto che rappresenta.

So I am confused which methods should go where

Di solito, viene utilizzata una classe astratta o una classe generica astratta invece di implementare direttamente l'interfaccia. Queste classi nella maggior parte dei casi servono come un modo più rapido per implementare l'interfaccia e risparmiare il codice boilerplate. Generics di solito si rivolgono alla sicurezza del tipo di una potenziale implementazione dell'interfaccia. È normale per loro introdurre nuovi metodi.

Un esempio classico è una classe astratta che implementa un'interfaccia ed esegue la maggior parte delle convalide e dei controlli, lasciando solo alcuni metodi astratti che devono contenere la logica dell'utente. Ciò ridurrebbe la maggior parte del codice boilerplate per l'implementazione concreta e punta direttamente alla funzionalità che non è stata implementata.
Le classi generiche di solito implementano la maggior parte dell'interfaccia con i cast e i controlli dei tipi e lasciano metodi simili, ora generici per l'implementazione finale, che vengono chiamati dai metodi di interfaccia non generici.

Oltre a quanto sopra, le classi base possono introdurre metodi specifici del contesto per l'implementazione, o metodi aggiuntivi che non sembrano appartenere al concetto di interfaccia. Ad esempio, quando si implementa un flusso di rete utilizzando un'interfaccia Stream immaginaria, l'implementazione può esporre metodi o proprietà per l'ubicazione di rete, l'url o altri dettagli di connessione. Questi sono specifici per NetworkStream e il contesto di rete e saranno fuori posto sull'interfaccia generica Stream , poiché potresti anche avere un'implementazione MemoryStream , che non ha nulla a che fare con i percorsi di rete.

Un altro motivo per cui una classe astratta deve introdurre nuovi metodi che non deve essere presente nelle interfacce è evitare le dipendenze del codice . L'interfaccia non deve attivare la dipendenza delle librerie che non sono direttamente correlate alla funzionalità coperta. Nella maggior parte dei casi, se ci si attiene al paradigma programming to interfaces , si farà riferimento alla libreria che ha l'interfaccia e si utilizza qualsiasi implementazione dell'interfaccia tramite esso, invece di lavorare direttamente con il tipo di implementazione. Ciò consentirà di evitare riferimenti diretti a qualsiasi libreria specifica di implementazione quando si lavora con l'interfaccia.

Sometimes I see class A extends Abstractuserclass and sometimes class A extends Abstractuserclass imlements useraInterface. What is the differnece if Abstractclass already impelements Userinterface

Non c'è alcuna differenza se si specifica l'interfaccia, se la classe base la implementa già. Suppongo che alcune persone lo facciano perché sono troppo prolisse, o perché questo rende il codice più leggibile. Leggendo la seguente riga di codice, non avrò bisogno di entrare nel Abstractuserclass per vedere che implementa il UseraInterface :

class A extends Abstractuserclass implements UseraInterface

Quindi, ciò risparmia un po 'di sforzo e rende il codice una sorta di auto-esplicativo.

    
risposta data 02.01.2014 - 11:09
fonte
0

Alcune risposte qui sono molto generiche affermando che un interface è un contratto. È difficile dire che sono un contratto quando non c'è spazio per interfacce e classi in PHP. Quindi è impossibile far rispettare il contratto. Non c'è nulla che impedisca a uno sviluppatore di creare un'istanza di una classe e di ignorare l'interfaccia. Quindi la descrizione del contratto non è chiara per gli sviluppatori che non hanno mai usato Java o C ++.

Quindi di cosa si tratta?

Il tuo esempio non è molto buono. UserInterface in AbstractUserClass in GenericUserClass in realtà non mostra i benefici di ciò che sta accadendo. Quindi permettimi di espanderlo un po '.

Perché abbiamo interfacce, perché un oggetto può implementare più interfacce. Pensa a un'interfaccia non come metodi ma come comportamento definito per una classe. L'interfaccia dice "Posso comportarmi come un utente ". Non importa come il codice raggiunge questo. Solo che supporta questa interfaccia.

La chiave per un buon design dell'interfaccia non è la creazione di interfacce multiuso. Lascia che ogni interfaccia definisca un singolo comportamento. Ciò rende molto più facile creare classi a scopo singolo che funzionano su questi tipi di oggetti. Invece di avere un UserInterface potresti espandere quello per includere AdminInterface , AuthenticatedUserInterface e UserPermissionsInterface . Quindi, più avanti nel tuo codice, puoi chiedere a un oggetto se supporta AdminInterface e, in tal caso, quell'utente è un amministratore.

Perché abbiamo classi astratte?

Interfacce, interfacce, interfacce ovunque ... Uno degli svantaggi nell'uso delle interfacce è che un'interfaccia non implementa assolutamente alcun codice. Per ogni interfaccia un programmatore deve implementare almeno una classe. Un buon framework che utilizza interfacce ti fornirà un'ampia gamma di classi astratte già implementate che ti danno un punto di partenza per l'utilizzo di tali interfacce. Un cattivo framework è uno che richiede molte interfacce e non ti dà nulla da cui partire.

Evita la classe astratta super che implementa molte interfacce e fornisce la maggior parte delle funzionalità per tali interfacce. È anche pesantemente ereditato da molte altre classi.

Quello che vuoi sono piccole classi astratte che implementano un'interfaccia da diverse prospettive. Potresti voler creare un paio di classi che implementano UserInterface come AbstractInvalidUser , AbstractAnonymousUser o AbstractAuthenticatedUser . In seguito potresti dover utilizzare Facebook per accedere a un utente. Puoi quindi creare un FaceBookUser che estende AbstractAuthenticatedUser . Ora è chiaro cosa fa ogni classe astratta, ma tutti implementano il UserInterface .

Quindi ora la grande domanda "Dove vanno a finire tutti i metodi?"

La chiave per creare codice sorgente gestibile è ridurre sempre le dipendenze fino alla loro parte più piccola. Ciò significa che non vuoi che il codice sorgente utilizzi FaceBookUser quando tutto ciò che sta facendo è visualizzare il nome dell'utente corrente. Se tutto nel codice sorgente utilizza FaceBookUser , è difficile spostare quella classe in un'altra cartella o rinominare quella classe. Per te è più semplice utilizzare UserInterface per visualizzare il nome di un utente e ora tutto quel codice sorgente non interessa se si tratta di Facebook, Google+ o del tuo accesso. La dipendenza è ridotta perché l'interfaccia è semplice e con un solo scopo.

Perché le interfacce separano il codice dall'implementazione dell'interfaccia. Al tuo altro codice non interessa se l'oggetto corrente è una classe wrapper, una classe delegate, una classe super pesante o solo una classe semplice. Per quanto riguarda il codice, è solo un riferimento UserInterface .

Le interfacce sono strumenti che consentono di creare codice di gestione. Posiziona i metodi nelle interfacce in modo da non creare dipendenze tra le classi. Puoi creare una separazione dalla tua dipendenza creando più di una interfaccia.

Questo è ciò che le persone intendono quando lo chiamano "contratto".

    
risposta data 03.01.2014 - 02:02
fonte
-1

Utilizza le interfacce per riutilizzare i contratti. Usa classi astratte per riutilizzare l'implementazione. La mia ipotesi è che AbstractUserClass fornisca un codice comune che richiederà tutte le implementazioni prevedibili di UserInterface .

    
risposta data 03.01.2014 - 00:58
fonte

Leggi altre domande sui tag