È sufficiente distinguere i metodi solo per nome dell'argomento (non per tipo)?


36

È sufficiente che i metodi siano distinti solo dal nome dell'argomento (non dal tipo) o è meglio denominarlo in modo più esplicito?

Per esempio T Find<T>(int id)vs T FindById<T>(int id).

C'è qualche buona ragione per nominarlo in modo più esplicito (cioè aggiungendo ById) vs mantenendo solo il nome dell'argomento?

Una delle ragioni a cui riesco a pensare è quando le firme dei metodi sono uguali ma hanno un significato diverso.

FindByFirstName(string name) e FindByLastName(string name)


4
Quindi, quando sovraccarichi Trova da includere T Find<T>(string name)o (int size)come prevedi di risolvere gli inevitabili problemi?
UKMonkey,

3
@UKMonkey quali inevitabili problemi?
Konrad,

3
nel primo caso: se più voci hanno lo stesso nome, è necessario modificare la firma della funzione; il che significa che le persone probabilmente si confonderanno con ciò che dovrebbe tornare; In quest'ultimo caso, l'argomento è lo stesso - e quindi un sovraccarico illegale. Puoi iniziare a nominare la funzione con "byX" o creare un oggetto per l'argomento in modo da poter avere l'equivalente di overload con lo stesso argomento. Funziona bene per situazioni diverse.
UKMonkey,

2
@UKMonkey puoi pubblicare una risposta con alcuni esempi di codice, se lo desideri
Konrad,

3
Gli ID dovrebbero probabilmente essere un IDoggetto opaco e non solo un int. In questo modo, verifica in fase di compilazione che non utilizzi un ID per un int o viceversa in una parte del codice. E con ciò puoi avere find(int value)e find(ID id).
Bakuriu,

Risposte:


68

Sicuramente c'è una buona ragione per nominarlo in modo più esplicito.

Non è principalmente la definizione del metodo che dovrebbe essere autoesplicativa, ma l' uso del metodo . E mentre findById(string id)e find(string id)sono entrambi auto-esplicativo, v'è una grande differenza tra findById("BOB")e find("BOB"). Nel primo caso sai che il letterale casuale è, in effetti, un ID. In quest'ultimo caso non sei sicuro - potrebbe effettivamente essere un determinato nome o qualcos'altro del tutto.


9
Tranne che nel 99% dei altri casi in cui si ha un nome di variabile o di proprietà è un punto di riferimento: findById(id)vs find(id). Puoi andare in entrambi i modi su questo.
Greg Burghardt,

16
@GregBurghardt: un valore particolare non è necessariamente chiamato allo stesso modo per un metodo e il suo chiamante. Ad esempio, considera di double Divide(int numerator, int denominator)essere utilizzati in un metodo: double murdersPerCapita = Divide(murderCount, citizenCount). Non puoi fare affidamento su due metodi usando lo stesso nome di variabile in quanto ci sono molti casi in cui non è così (o quando è il caso, è una cattiva denominazione)
Flater,

1
@Flater: dato il codice nella domanda (trovare cose da una sorta di memoria persistente) Immagino che potresti chiamare questo metodo con un "assassinatoCitizenId" o "citizenId" ... Non credo davvero che l'argomento o i nomi delle variabili siano ambiguo qui. E onestamente potrei andare in entrambi i modi su questo. In realtà è una domanda molto supponente.
Greg Burghardt,

4
@GregBurghardt: non è possibile distillare una regola globale da un singolo esempio. La domanda di OP è in generale , non specifica dell'esempio fornito. Sì, ci sono alcuni casi in cui l'uso dello stesso nome ha senso, ma ci sono anche casi in cui non lo è. Da qui questa risposta, è meglio puntare alla coerenza anche se non è necessario in un sottoinsieme di casi d'uso.
Flater,

1
I parametri dei nomi risolvono la confusione dopo che la confusione è già esistita , poiché è necessario esaminare la definizione del metodo, mentre i metodi con nome esplicito evitano completamente la confusione avendo il nome presente nella chiamata del metodo. Inoltre, non puoi avere due metodi con lo stesso nome e lo stesso tipo di argomento ma un nome di argomento diverso, il che significa che avrai bisogno di nomi espliciti in alcuni casi comunque.
Darkhogg,

36

Vantaggi di FindById () .

  1. A prova di futuro : se si inizia con Find(int), e poi devono aggiungere altri metodi ( FindByName(string), FindByLegacyId(int), FindByCustomerId(int), FindByOrderId(int), ecc), le persone come me tendono a spendere le età in cerca di FindById(int). Non è un problema se si può e cambierà Find(int)per FindById(int)una volta si rende necessario - futuro proofing è su queste se s.

  2. Più facile da leggere . Findva benissimo se la chiamata sembra record = Find(customerId);Eppure FindByIdè leggermente più facile da leggere se lo è record = FindById(AFunction());.

  3. Coerenza . È possibile applicare coerentemente il FindByX(int)/ FindByY(int)pattern ovunque, ma Find(int X)/ Find(int Y)non è possibile perché sono in conflitto.

Vantaggi di Find ()

  • BACIO. Findè semplice e diretto e accanto operator[]è uno dei 2 nomi di funzioni più attesi in questo contesto. (Alcune alternative popolari di essere get, lookupo fetch, a seconda del contesto).
  • Come regola generale, se si dispone di un nome di funzione che è una singola parola ben nota che descrive accuratamente ciò che la funzione fa, usarla. Anche se esiste un nome a più parole più lungo che è leggermente migliore nel descrivere ciò che fa la funzione. Esempio: lunghezza vs NumberOfElements . C'è un compromesso e dove tracciare la linea è oggetto di un dibattito in corso.
  • È generalmente buono evitare la ridondanza. Se guardiamo FindById(int id), possiamo facilmente rimuovere la ridondanza cambiandola in Find(int id), ma c'è un compromesso: perdiamo un po 'di chiarezza.

In alternativa puoi ottenere i vantaggi di entrambi usando ID fortemente tipizzati:

CustomerRecord Find(Id<Customer> id) 
// Or, depending on local coding standards
CustomerRecord Find(CustomerId id) 

Implementazione di Id<>: valori ID fortemente digitati in C #

I commenti qui, così come nel link sopra, hanno sollevato molteplici preoccupazioni riguardo a Id<Customer>ciò che vorrei affrontare:

  • Preoccupazione 1: è un abuso di generici. CustomerIde OrderIDsono tipi diversi ( customerId1 = customerId2;=> buono, customerId1 = orderId1;=> cattivo), ma la loro implementazione è quasi identica, quindi possiamo implementarli con copia incolla o con metaprogrammazione. Mentre in una discussione c'è valore sull'esporre o nascondere il generico, la metaprogrammazione è ciò a cui servono i generici.
  • Preoccupazione 2: non impedisce errori semplici. / È una soluzione alla ricerca di un problema. Il problema principale che viene rimosso utilizzando ID fortemente tipizzati è l'ordine degli argomenti errato in una chiamata a DoSomething(int customerId, int orderId, int productId). Gli ID fortemente tipizzati impediscono anche altri problemi, incluso quello richiesto.
  • Preoccupazione 3: oscura davvero il codice. È difficile dire se un ID è trattenuto int aVariable. È facile dire che un ID è conservato Id<Customer> aVariablee possiamo persino dire che è un ID cliente.
  • Preoccupazione 4: questi ID non sono tipi forti, ma solo wrapper. Stringè solo un involucro in giro byte[]. Il wrapping o l'incapsulamento non è in conflitto con la tipizzazione forte.
  • Preoccupazione 5: è troppo ingegnerizzato. Ecco la versione minima, anche se ti consiglio di aggiungere operator==e operator!=, se non vuoi affidarti esclusivamente a Equals:

.

public struct Id<T>: {
    private readonly int _value ;
    public Id(int value) { _value = value; }
    public static explicit operator int(Id<T> id) { return id._value; }
}

10

Un altro modo di pensare a questo è usare la sicurezza del tipo della lingua.

È possibile implementare un metodo come:

Find(FirstName name);

Dove FirstName è un oggetto semplice che avvolge una stringa che contiene il nome e significa che non può esserci confusione su ciò che sta facendo il metodo, né sugli argomenti con cui viene chiamato.


4
Non sono sicuro di quale sia la tua risposta alla domanda dei PO. Mi consiglia di utilizzare un nome come "Trova" facendo affidamento sul tipo di argomento? Oppure mi consiglia di utilizzare tali nomi solo quando esiste un tipo esplicito per gli argomenti e di utilizzare un nome più esplicito come "FindById" altrove? Oppure mi consiglia di introdurre tipi espliciti per rendere più fattibile un nome come "Trova"?
Doc Brown,

2
@DocBrown Penso che sia quest'ultimo e mi piace. In realtà è simile alla risposta di Peter, iiuc. La logica, per quanto ho capito, è duplice: (1) Dal tipo di argomento è chiaro cosa fa la funzione; (2) Non è possibile commettere errori come string name = "Smith"; findById(name);.che è possibile se si utilizzano tipi generali non descrittivi.
Peter - Ripristina Monica il

5
Mentre in generale sono un fan della sicurezza del tipo di tempo di compilazione, fai attenzione ai tipi di wrapper. L'introduzione di classi wrapper per motivi di sicurezza a volte può complicare gravemente l'API se eseguita in eccesso. ad esempio, l'intero problema "artista precedentemente noto come int" che Winapi ha; a lungo termine, direi che la maggior parte delle persone guarda all'infinito DWORD LPCSTRcloni ecc. e pensa "è un int / stringa / ecc.", arriva al punto in cui passi più tempo a sostenere i tuoi strumenti che a progettare il codice .
jrh

1
@jrh True. La mia cartina di tornasole per l'introduzione di tipi "nominali" (che differiscono solo nel nome) è quando le funzioni / i metodi comuni non hanno alcun senso nel nostro caso d'uso, ad esempio le ints sono spesso sommate, moltiplicate, ecc. Che non ha senso per gli ID; quindi farei IDdistinto da int. Questo può semplificare un'API, restringendo ciò che possiamo fare dato un valore (ad esempio se ne abbiamo uno ID, funzionerà solo con find, non ad esempio ageo highscore). Al contrario, se ci troviamo a convertire molto o a scrivere la stessa funzione / metodo (es. find) Per più tipi, questo è un segno che le nostre distinzioni sono troppo fini
Warbo,

1

Voterò per una dichiarazione esplicita come FindByID .... Il software dovrebbe essere creato per il cambiamento. Dovrebbe essere aperto e chiuso (SOLIDO). Quindi la classe è aperta per aggiungere un metodo di ricerca simile come diciamo FindByName ... ecc.

Ma FindByID è chiuso e la sua implementazione è testata dall'unità.

Non suggerirò metodi con predicati, quelli sono buoni a livello generico. Che cosa succede se in base al campo (ByID) hai una metodologia diversa completa.


0

Sono sorpreso che nessuno abbia suggerito di usare un predicato come il seguente:

User Find(Predicate<User> predicate)

Con questo approccio non solo riduci la superficie dell'API ma dai anche più controllo all'utente che la utilizza.

Se ciò non bastasse, puoi sempre espanderlo alle tue esigenze.


1
Il problema è che è meno efficiente a causa del fatto che non può trarre vantaggio da cose come gli indici.
Solomon Ucko,
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.