Qual è la logica per rendere statici determinati metodi per i tipi di dati?
In C#, ad esempio, esistono metodi statici per stabilire se una stringa è null o vuota , trovare un elemento in un array, cancellare un array, ecc. Tuttavia esiste un metodo di istanza per sostituire i caratteri in una stringa e sia statico che istanza metodi per copiare elementi tra array.
Allo stesso modo, in Python ci sono funzioni per ottenere la lunghezza di un elenco, per filtrare un elenco, ecc., ma metodi di istanza per trovare l'indice di un elemento e per copiare l'elenco.
Al contrario, in JavaScript, praticamente qualsiasi operazione che devo eseguire su un tipo di dati integrato sarà accessibile tramite il metodo o la proprietà dell'istanza.
Quindi qual è la logica per rendere certe cose statiche e altre no?
EDIT : Per essere chiari, capisco lo scopo dei metodi statici. La mia domanda è più sul motivo per cui alcuni metodi sono scritti come statici quando avrebbero potuto essere scritti come metodi di istanza. Come Find
per gli array e Format
per le stringhe in C# filter
e len
in Python.
Risposte
EDIT : la domanda è stata perfezionata, quindi perfezionerò la mia risposta, ma lascerò la precedente sotto per non rendere i commenti irrilevanti.
Ci possono essere diversi motivi per implementare un metodo come metodo statico invece di un metodo di istanza, con linguaggi o framework diversi che possono avere motivazioni diverse:
L'interfaccia pubblica di un oggetto è un contratto. Se il tuo
Array
tipo espone unSort
metodo, ogni array d'ora in poi, indipendentemente dal tipo o dalla versione del linguaggio, deve supportarlo. Se, tuttavia, anArray
è solo una rappresentazione di dati in memoria con un contratto sottile e la logica viene spostata in una classe comeArray.Sort
, ti offre maggiore flessibilità per estendere o modificare il tuo comportamento senza interrompere la compatibilità.Vedi questa citazione da Clean Code di Bob Martin :
Gli oggetti espongono il comportamento e nascondono i dati; La struttura dei dati espone i dati e non ha un comportamento significativo"
In questo caso, la List
classe è una classe che espone il comportamento: nasconde i suoi dati (è implementata come un array ridimensionabile, ma questo è un dettaglio di implementazione), ma espone la funzionalità. An int[]
, tuttavia, è una struttura dati. int
È un blocco allocato in sequenza . Ha una Length
proprietà perché fa parte dei suoi dati , ma non ha molto comportamento: il comportamento viene scaricato su una classe che opera su di esso.
- Potrebbe essere una scelta stilistica. Python non è un linguaggio puramente OO e potrebbe non avere la garanzia Java/C# "tutto è un oggetto", quindi ha scelto di utilizzare funzioni del linguaggio come
len()
quelle che possono essere utilizzate indipendentemente dal fatto che il suo parametro sia o meno un'istanza di oggetto.
Risposta precedente
La logica alla base dei metodi statici in C# è avere metodi legati a un tipo ma non a un'istanza . Ci possono essere diverse ragioni per questo:
- I metodi che possono operare sia su un'istanza che su
null
, comestring.IsNullOrEmpty
, naturalmente non possono essere metodi di istanza, perché potrebbe non esserci un'istanza. - Allo stesso modo, i metodi factory che creano un'istanza non hanno alcuna istanza da cui essere chiamati. Ad esempio, il metodo statico deprecato da qualcuno
WebRequest.Create()
restituisce una nuova istanza di un tipo derivato daWebRequest
. - Sebbene sia facile dimenticarlo, .NET ha una gestione diversa per un tipo integrale nativo (
int i =5
) e un tipo di riferimento boxed attorno ad esso (object boxed = 5
). Se chiami un metodo su un tipo di valore (int i = 5; t.ToString();
), ti viene addebitato un costo per il boxing. Ecco perché alcune operazioni non sono definite come un metodo su un oggetto, ma come un metodo statico che accetta un valore. Ecco perché operazioni matematiche comeMath.Abs
non sono definite come metodi sui tipi boxed, ma come funzioni statiche che ricevono e restituiscono valori. - A volte i metodi statici vengono utilizzati per "configurare" un tipo, non un'istanza specifica ma il comportamento di quel tipo nel sistema. Ad esempio,
[ServicePointManager.SetTcpKeepAlive()][1]
configura il comportamento di tutte le classi che utilizzano lo stack HTTP(s) condiviso in .NET, in modo da poterle configurare tutte tramite questi metodi statici.
I metodi statici hanno molto senso
- quando il metodo è correlato al tipo ma non richiede un'istanza esistente; o
- quando la lingua supporta lo spazio dei nomi solo tramite le classi.
C# supporta solo funzioni come membri di classi, quindi le funzioni helper devono essere scritte come metodi statici. La classe C# Math
è un ottimo esempio.
JavaScript non ha classi, ma ogni variabile non locale è associata a qualche oggetto: nel browser, la maggior parte delle API è accessibile tramite l' window
oggetto. Tuttavia, ci sono metodi accessibili tramite un oggetto simile a una classe, ad esempio Array.isArray(x)
.
Python ha un design peculiare che è per lo più orientato agli oggetti, ma ha alcune funzioni integrate come len()
o iter()
, in gran parte per ragioni storiche. Sotto il cofano, invocano metodi reali, ad esempio iter(x)
is di solito x.__iter__()
, con un fallback per oggetti che possono essere indicizzati come un elenco.
Più interessanti sono i metodi che static
non sono dovuti a stranezze della lingua, ma perché hanno senso. I metodi di tipo costruttore o di fabbrica sono il classico esempio in cui non abbiamo un oggetto esistente su cui poter chiamare un metodo. Alcune funzioni inoltre non operano esattamente su un oggetto, ad esempio String.IsNullOrEmpty(x)
opera su zero o uno oggetti. Questo non funziona come metodo, ad esempio x.IsNullOrEmpty()
genererebbe un'eccezione se è nullo! Il Array.isArray(x)
metodo in JS è un altro ottimo esempio: vogliamo chiamare isArray(x)
oggetti di qualsiasi tipo, non solo array. Pertanto, un metodo di istanza non funziona a meno che non faccia parte del prototipo di ogni oggetto.