Qual è la differenza tra "a" e "come" prefissi del nome del metodo come
- toList (),
- asList (),
- ecc.
Quando utilizzarlo durante la progettazione di un metodo?
Qual è la differenza tra "a" e "come" prefissi del nome del metodo come
Quando utilizzarlo durante la progettazione di un metodo?
Si prevede che una funzione toXYZ()
esegua una conversione e che restituisca un nuovo oggetto indipendente (sebbene l'immutabilità consenta l'ottimizzazione, java.lang.String.toString()
restituisce appena l'oggetto).
Ad esempio, in C ++ abbiamo std::bitset::to_ulong()
che può facilmente fallire, e un intero pletora di to_string()
, tutti facendo un (più o meno) conversione complessa e allocazione della memoria.
Si suppone che una funzione asXYZ()
restituisca una vista (potenzialmente diversa) dell'origine, eseguendo un lavoro minimo.
Come esempio, in C ++ abbiamo std::as_const()
che restituisce solo un riferimento costante e il più coinvolto std::forward_as_tuple
che si riferisce anche ai suoi argomenti per riferimento.
Onestamente, potrebbe essere solo nominare l'incoerenza. Se si guardano gli oggetti della libreria standard in Smalltalk, ad esempio, tutti i metodi che eseguono "conversioni complesse" o "restituiscono rappresentazioni semplici in un altro tipo" hanno come prefisso as
, come in asString
, asFloat
, asSeconds
, e il metodo standard per convertire qualsiasi cosa in qualsiasi altra cosa, as: aClass
.
In Ruby, gli stessi tipi di metodi sono prefissati con to_
, come in to_s
, to_a
, to_h
, abbreviazione di string
, array
e hash
rispettivamente.
Nessuna delle librerie standard sembra differenziare tra diversi tipi di conversioni, probabilmente perché dovrebbe essere considerata come un dettaglio di implementazione.
Tuttavia, in Java vediamo un sacco di confusione. Come hai detto, c'è toString
, asList
e così via. Credo che queste siano solo un'incoerenza di denominazione, perché se provi a definire un significato diverso per ogni prefisso, troverai sempre un contro-esempio da qualche altra parte nella libreria standard.
Quindi, in ogni caso, direi che l'importante è che tu e il tuo team scegliete un prefisso e usatelo in modo coerente per tutto il codice. La coerenza è la chiave, quindi le persone non sono lasciate a meravigliarsi, come se fosse necessario.
Anche se esiste già una risposta accettata, sembra concentrarsi su C ++, mentre la domanda è contrassegnata con java . In Java, il primo esempio che viene in mente per questo genere di cose è Arrays.asList , che restituisce, essenzialmente, una vista di un array, racchiuso in un elenco. L'array sottostante e l'elenco sono ancora connessi; le modifiche all'array si riflettono nell'elenco e viceversa. Tuttavia, l'array restituito dal metodo toArray della lista è indipendente dall'array originale e dall'elenco:
String[] wordArray = {"one", "fine", "day"};
List<String> wordList = Arrays.asList(wordArray);
// changes to the array are visible in the list
System.out.println(wordList); // prints "[one, fine, day]"
wordArray[1] = "horrible";
System.out.println(wordList); // prints "[one, horrible, day]"
// changes to the list are visible in the array
wordList.set(1, "beautiful");
System.out.println(wordArray[1]); // prints "beautiful"
// but changes to the list or array don't affect the
// result from the list's toArray method.
String[] moreWords = wordList.toArray(new String[] {});
wordList.set(0, "the");
wordArray[1] = "best";
for (int i=0; i<3; i++) {
System.out.println(moreWords[i]); // prints "one", "beautiful", and "day"
}
Tutto ciò detto, non c'è alcuna garanzia che ogni sviluppatore di librerie segua questa convenzione, quindi è ancora necessario controllare la documentazione per scoprire se questo è il comportamento che si otterrà dal codice sconosciuto.
L'altro posto in cui ho visto i metodi as ... () utilizzati frequentemente è nei tipi downcasting di sottotipi. Ad esempio, se si dispone di un insieme enumerato di sottotipi, si potrebbe finire con un codice del tipo:
/**
* Every Node is either an ANode or a BNode.
*/
interface Node {
/**
* Returns this Node as an ANode.
*
* @return this node
*/
default ANode asANode() {
if (this instanceof ANode) {
return (ANode) this;
}
else {
throw new UnsupportedOperationException();
}
// Or, in Java8 style, perhaps:
// return Optional.of(this)
// .filter(ANode.class::isInstance)
// .map(ANode.class::cast)
// .orElseThrow(UnsupportedOperationException::new);
}
/**
* Returns this Node as a BNode.
*
* @return this node
*/
default BNode asBNode() {
if (this instanceof BNode) {
return (BNode) this;
}
else {
throw new UnsupportedOperationException();
}
}
}
La differenza che ho notato (solo ora a pensarci su) è
Quindi vediamo AsInteger e AsString e vediamo ToArray e ToStringList.
Implica una conversione, che ha senso (è un movimento, un processo). Come implica una rappresentazione, un modo di esprimere l'oggetto originale.
Un altro modo per cercare questo:
E poi c'è "arte nota" (o eredità) da trattare. Prima che le lingue fossero completamente OO da zero avresti funzioni di libreria come StrToInt () e IntToStr (). Hanno eseguito conversioni, erano operazioni quindi era logico chiamarle SomethingToSomethingelse (). Dopotutto, To è più attivo di As. Sto pensando in particolare a Delphi qui.
Quando C # è stato progettato con l'ambizione di portare OO fino in fondo, aveva senso avere un metodo sull'oggetto ora intero che convertisse il numero intero in una stringa. Anche se abbiamo anche una classe di conversione, la conversione in stringa è così comune che è stato creato un metodo virtuale sull'oggetto. I progettisti potrebbero aver capito che ToString sarebbe stato più familiare alle persone dal vecchio paradigma e forse questo è il motivo per cui abbiamo ottenuto un metodo virtuale ToString () e non una proprietà virtuale AsString.