I nomi delle interfacce dovrebbero iniziare con un prefisso "I"?


73

Ho letto " Clean Code " di Robert Martin per diventare un programmatore migliore. Sebbene finora nulla di tutto ciò sia stato davvero rivoluzionario, mi ha fatto riflettere diversamente sul modo in cui progetto applicazioni e scrivo codice.

C'è una parte del libro che non solo non sono d'accordo, ma non ha senso per me, in particolare per quanto riguarda le convenzioni di denominazione delle interfacce. Ecco il testo, preso direttamente dal libro. Ho audace l'aspetto di questo che trovo confuso e vorrei chiarimenti.

Preferisco lasciare le interfacce disadorno. L'I precedente, così comune nelle mazzette di oggi, è una distrazione nella migliore delle ipotesi e troppe informazioni nella peggiore. Non voglio che i miei utenti sappiano che sto consegnando loro un'interfaccia .

Forse è perché sono solo uno studente, o forse perché non ho mai fatto alcuna programmazione professionale o di gruppo, ma vorrei che l'utente sapesse che si tratta di un'interfaccia. C'è una grande differenza tra l'implementazione di un'interfaccia e l'estensione di una classe.

Quindi, la mia domanda si riduce a "Perché dovremmo nascondere il fatto che una parte del codice si aspetta un'interfaccia?"

modificare

In risposta a una risposta:

Se il tuo tipo è un'interfaccia o una classe è la tua attività, non l'attività di qualcuno che utilizza il tuo codice. Quindi non dovresti perdere i dettagli del tuo codice in questo codice di terze parti.

Perché non dovrei "trapelare" i dettagli se un determinato tipo è un'interfaccia o una classe in codice di terze parti? Non è importante per lo sviluppatore di terze parti che utilizza il mio codice sapere se implementeranno un'interfaccia o estenderanno una classe? Le differenze non sono così importanti come le sto immaginando di essere nella mia mente?


3
Sono d'accordo con il tuo punto. C'è un momento in cui troppe informazioni nascoste non sono molto utili. Tuttavia, anche se segui questa linea guida, sarai comunque in grado di dire il tipo usando l'IDE o un componente aggiuntivo.
NoChance,

3
Questa domanda è essenzialmente conosciuta come "notazione ungherese", dovresti trovare molti argomenti e il motivo per cui la maggior parte degli sviluppatori non-MS l'ha abbandonata con questa parola chiave. La notazione ungherese era prevalentemente prevalente per le variabili, ma è essenzialmente la stessa per i tipi.
thiton

2
La modifica del titolo precedente era terribile. Questa domanda non riguarda la notazione ungherese in generale semplicemente perché menziona una convenzione che potrebbe essere associata ad essa. I meriti relativi di HN sono del tutto irrilevanti qui; la domanda riguardava specificamente le interfacce rispetto alle classi e se le differenze semantiche sono o meno abbastanza importanti / interessanti da giustificare una convenzione di denominazione per casi speciali.
Aaronaught,

Ri to know whether they will be implementing an interface or extending a class: sì, ma la maggior parte degli utenti del tuo codice lo chiamerà, non lo implementerà o lo estenderà, e non gliene potrebbe fregare di che si tratta.
david.pfx,

Per quello che vale, preferisco enormemente il prefisso "io". Uso anche un prefisso "Astratto" su classi astratte per lo stesso motivo. Non fa differenza per i consumatori della classe / interfaccia, ma può fare una grande differenza per coloro che hanno bisogno di fornirne istanze, e lo rende anche molto più semplice per gli altri sviluppatori che stanno leggendo il tuo codice. Significa che possono vedere a colpo d'occhio con cosa hanno a che fare, invece di dover consultare l'IDE caso per caso per ulteriori informazioni. Ho appena iniziato ad usare Angular e trovo davvero fastidioso che non seguano questa convenzione!
Dan King,

Risposte:


67

Se ti fermi a pensarci, vedrai che un'interfaccia non è davvero semanticamente molto diversa da una classe astratta:

  • Entrambi hanno metodi e / o proprietà (comportamento);
  • Nessuno dei due dovrebbe avere campi non privati ​​(dati);
  • Nessuno dei due può essere istanziato direttamente;
  • Derivare da uno significa implementare tutti i metodi astratti che ha, a meno che anche il tipo derivato non sia astratto.

In effetti, le distinzioni più importanti tra classi e interfacce sono:

  • Le interfacce non possono avere dati privati;
  • I membri dell'interfaccia non possono avere modificatori di accesso (tutti i membri sono "pubblici");
  • Una classe può implementare più interfacce (invece di essere generalmente in grado di ereditare da una sola classe di base).

Poiché le uniche distinzioni particolarmente significative tra classi e interfacce ruotano attorno a (a) dati privati ​​e (b) gerarchia di tipi - nessuno dei quali fa la minima differenza per un chiamante - non è generalmente necessario sapere se un tipo è un'interfaccia o una classe. Non hai certamente bisogno dell'indicazione visiva .

Tuttavia, ci sono alcuni casi angolari di cui tenere conto. In particolare, se stai usando la riflessione, l'intercettazione, i proxy / mixin dinamici, la tessitura bytecode, la generazione di codice o qualsiasi cosa che coinvolga direttamente il sistema di battitura o il codice dell'ambiente stesso, allora è molto utile e talvolta necessario sapere subito pipistrello se hai a che fare con un'interfaccia o una classe. Chiaramente non vuoi che il tuo codice fallisca misteriosamente perché hai provato ad aggiungere una classe, piuttosto che un'interfaccia, come un mixin.

Per un tipico codice di logica aziendale semplice e vaniglia, tuttavia, non è necessario pubblicizzare le distinzioni tra classi astratte e interfacce perché non entreranno mai in gioco.

Detto questo, tendo a anteporre comunque le mie interfacce C # Iperché quella è la convenzione .NET utilizzata e sostenuta da Microsoft. E quando sto spiegando le convenzioni di codifica a un nuovo sviluppatore, è molto meno seccante usare le regole di Microsoft piuttosto che spiegare perché abbiamo le nostre regole "speciali".


Grazie per questa rottura. +1 per le "significative distinzioni tra classi e interfacce ..."
Charles Sprayberry,

24
+1 per "Detto questo, tendo a mettere in prefisso le mie interfacce C # con me perché questa è la convenzione .NET utilizzata e sostenuta da Microsoft". Questo è un motivo sufficiente per me in C #. Seguendo questo standard comune è più probabile che altri programmatori .NET identifichino l'interfaccia.
Robotsushi,

4
Come qualcuno che scrive sia Java che C # l'affermazione "Tutto ciò che viene detto, tendo a mettere in prefisso le mie interfacce C # con me comunque perché quella è la convenzione .NET utilizzata e sostenuta da Microsoft" non può essere sottovalutata - è una delle cose che mi aiuta a ricordare in quale lingua sto lavorando,
Aidos,

3
Un'altra differenza in .NET (ma non in Java) è che molti linguaggi .NET non consentono alle interfacce di contenere metodi statici, quindi hackerare Iun'interfaccia può dare un buon nome a una classe per contenere metodi statici associati all'interfaccia (ad es. Enumerable<T>.Emptyo Comparer<T>.Default).
supercat

Ho una preoccupazione. In C ++ se apri un'intestazione e vedi che si class Fooestende già class Bar, non è immediatamente ovvio se class Barviene esteso o implementato. Quindi ora devi andare a cercare class Barnell'intestazione per decidere se va bene modificarlo class Fooper estenderlo class Baz. Inoltre, il prefisso I fornisce un chiaro indizio visivo se la "classe di base singola" viene violata o meno - quindi si ottiene un controllo di integrità ogni volta che si apre un'intestazione.
jsj

14

In molti modi, la coerenza è più importante della convenzione. Finché sei coerente nei tuoi schemi di denominazione, non sarà difficile lavorare con loro. Il prefisso si interfaccia con un I, se vuoi, o lascia semplicemente il nome disadorno, non mi importa se scegli uno stile e ti attieni!


2
+1 per coerenza> convenzione. In C # avresti il ​​prefisso con un I e in Java non lo faresti. Compiti diversi richiedono convenzioni diverse
Bringer128,

2
+1 mentre personalmente preferirei non avere Is sulle mie interfacce, essere incoerente con il BCL e le librerie di terze parti utilizzate nei progetti .Net non è un'opzione imho.
jk.

13

Il libro è pieno di cose buone, ma aggiungerei ancora "I" al nome dell'interfaccia.

Immagina di avere public interface ILogun'implementazione predefinita public class Log.

Ora, se decidi di farlo public interface Log, all'improvviso devi cambiare la classe Log in public class LogDefaulto qualcosa del genere. Sei passato dall'avere un personaggio in più a sette - sicuramente è uno spreco.

C'è spesso una linea sottile tra teoria e pratica. In teoria questa è davvero una buona idea, ma in pratica non è così buona.


Questo è anche menzionato nella documentazione
levininja,

30
"Immagina di avere un'interfaccia pubblica ILog con un registro di classe pubblica di implementazione predefinito." - Quindi hai un nome scadente per l'implementazione predefinita. Cosa fa Log? Accedi alla console? Syslog? Verso il nulla? Questi dovrebbero essere chiamati ConsoleLog, SyslogLoge NullLog, rispettivamente. Lognon è un buon nome per una classe concreta, perché non è un nome concreto.
Sebastian Redl,

7
Personalmente questo è esattamente il motivo per cui odio vedere ITheClassName, stai solo creando un'interfaccia senza motivo, è solo rumore sopra TheClassName. Se la tua interfaccia ha uno scopo, dovrebbe essere possibile dargli un nome migliore di ITheClassName
Richard Tingle,

Il nome della classe viene utilizzato una volta (per creare un'istanza), il nome dell'interfaccia viene utilizzato ovunque. L'aggiunta di una lettera all'interfaccia per salvare la lettera sul nome della classe è falsa economia (di caratteri).
sergut,

@RichardTingle Ma penso che l'uso comune ci sia semplicemente quello che MyClassha una variazione beffarda , giusto? Cioè, usiamo spesso interfacce per fare essenzialmente i metodi pubblici veramente pubblico in un modo che consente per il test. (Non dire che è buono o cattivo, ma capire che la motivazione per le interfacce è spesso semplicemente rendere le lezioni che sono dipendenze facilmente derisabili spiega l'1 a 1 MyClassai IMyClassmapping.)
ruffin

8

Bene, non si tratta di implementare l'interfaccia o di estendere una classe. In questi casi, sai comunque cosa stai facendo.

Tuttavia, quando un codice di terze parti (un altro modulo dell'applicazione per esempio) manipola i tuoi dati, questo codice non dovrebbe interessarti se stai presentando un'interfaccia o una classe.

Questo è il punto centrale dell'astrazione. Stai presentando a questo codice di terze parti un oggetto di un determinato tipo. Questo dato tipo ha alcune funzioni membro che puoi chiamare. È abbastanza.

Se il tuo tipo è un'interfaccia o una classe è la tua attività, non l'attività di qualcuno che utilizza il tuo codice. Quindi non dovresti trasferire i dettagli del tuo codice a questo codice di terze parti.

A proposito, interfacce e classi sono tipi di riferimento alla fine. E questo è ciò che conta. Quindi questo è ciò che la tua convenzione di denominazione deve sottolineare.


@Mat> Sì, è stato un mio errore e l'ho modificato.
deadalnix,

5

La maggior parte delle risposte sembra supporre che il linguaggio di programmazione sia Java o C # in cui esiste un concetto (o parola chiave) per interfacce / classi astratte. In questi casi, in un IDE decente, è immediatamente visibile con quale tipo di classe uno si occupa e la scrittura public interface IMyClassè una duplicazione non necessaria. È come se non scrivessi public final FinalMyClass: immagina di inserire ogni parola chiave nel nome della classe.

Tuttavia, secondo me in C ++, la situazione è leggermente diversa in quanto si deve immergersi nel file di intestazione e vedere se la classe ha metodi virtuali per scoprire se si tratta di una classe astratta. In tal caso, posso vedere chiaramente un argomento per la scrittura class AbstractMyClass.

Quindi la mia risposta sarebbe: dipende dal linguaggio di programmazione.

Aggiornato 2016: 2 anni di esperienza in C ++ in seguito ora probabilmente prenderei in considerazione una cattiva pratica con prefisso ai nomi delle classi Abstract, anche se direi che probabilmente ci sono basi di codice che sono così complesse che potrebbe avere senso.


Sei sicuro che questo risponda alla domanda? Potresti leggere le altre risposte e chiarire alcuni dei tuoi punti.
david.pfx,

C ++ ha sicuramente delle interfacce, anche se non ci sono parole chiave per esso. Come si chiama una classe in cui ogni funzione membro è pura virtuale e non ci sono variabili membro?

@ david.pfx: Penso di rispondere esattamente alla domanda "I nomi delle interfacce dovrebbero iniziare con un prefisso" I "?": Dipende dal linguaggio di programmazione. Se pensi che la mia elaborazione non sia abbastanza chiara, sono felice di migliorarla - quali parti non sono chiare per te?
Ela782

2
@JohnGaughan: Sicuro che ha interfacce! Ma il mio punto è che si tratta della parola chiave. C ++ non ne ha uno, quindi è non è visibile per l'IDE / utente se lui / lei si occupa di un'interfaccia o meno. In Java, tuttavia, è immediatamente visibile.
Ela782,

1
Leggi la risposta più votata e confronta la tua. Sei nuovo di SO, e questa è un'opportunità per imparare quali tipi di risposte attirano voti. Così com'è, il tuo no. Con più lavoro, più spiegazione, più valore, potrebbe proprio. Tenere duro!
david.pfx,

4

Segui i modi di dire della lingua che stai utilizzando.

Ogni lingua ha i suoi modi di dire e le sue linee guida di codifica. Ad esempio, in C #, è idiomatico il prefisso delle interfacce con I. In Go, non lo è.


+1 Segui i modi di dire della lingua che stai utilizzando. Ovviamente l'autore del libro è uno sviluppatore Java. .NET / C #: ILog Java: Log Ma lo svantaggio: quando mi piace avere una classe Log che implementa l'interfaccia ILog .... MyLog, LogDefault, LogBase, ...? Brutto, vero? Più brutto è: LogImpl ....: D
hfrmobile l'

0

Nel mio codice (Java), tendo ad avere questa convenzione nelle API che espongo ai chiamanti:

  • La funzionalità è fornita attraverso le interfacce e mai dalle classi.
  • Le parti non funzionali sono classi.

Per non funzionale, intendo cose come strutture di dati puri (come classi che fungono da ponti per la serializzazione XML o ORM), enumerazioni ed eccezioni, tutte che non possono essere interfacce (bene, è possibile per le classi di dati puri , ma è un sacco di lavoro per un guadagno molto piccolo in quanto non c'è nulla che quelle classi facciano tranne i dati di conservazione).

In termini di convenzioni di denominazione, le interfacce tendono a mappare su nomi di attori (ad es. FooBarWatcher) O aggettivi (ad es. FooBarWatchable) E sia le classi di dati puri che le enumerazioni si associano a nomi non attivi (ad es. FooBarStatus); l'IDE può guidare il consumatore API senza speciali convenzioni di denominazione. Eccezioni seguono solite convenzioni Java ( FooBarException, FooBarError, FooBarFault), naturalmente.

Metterò spesso anche le interfacce nel loro pacchetto o anche nella loro libreria, solo per assicurarmi di non essere tentato di infrangere le mie regole. (Questo mi aiuta anche a gestire la build quando sto derivando il codice da fonti esterne come i documenti WSDL.)


Inoltre non ho mai messo Iprefissi sulle interfacce. Di Naturalmente si tratta di un'interfaccia! È in un'API (o SPI)!
Donal Fellows,
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.