¿Cuál es la razón para hacer que ciertos métodos para tipos de datos sean estáticos?
En C#, por ejemplo, hay métodos estáticos para saber si una cadena es nula o vacía , encontrar un elemento en una matriz, borrar una matriz, etc. Sin embargo, hay un método de instancia para reemplazar caracteres en una cadena, y tanto estático como de instancia. Métodos para copiar elementos entre arreglos.
De manera similar, en Python hay funciones para obtener la longitud de una lista, para filtrar una lista, etc., pero también hay métodos de instancia para encontrar el índice de un elemento y para copiar la lista.
Por el contrario, en JavaScript, prácticamente cualquier operación que necesite hacer en un tipo de datos incorporado será accesible a través del método o la propiedad de la instancia.
Entonces, ¿cuál es la razón para hacer que ciertas cosas sean estáticas y otras no?
EDITAR : para ser claros, entiendo el propósito de los métodos estáticos. Mi pregunta es más sobre por qué ciertos métodos se escriben como estáticos cuando podrían haberse escrito como métodos de instancia. Como Find
para arreglos y Format
cadenas en C#, filter
y len
en Python.
Respuestas
EDITAR : la pregunta se ha refinado, por lo que refinaré mi respuesta, pero dejaré la anterior a continuación para que los comentarios no sean irrelevantes.
Puede haber varias razones para implementar un método como un método estático en lugar de un método de instancia, con diferentes lenguajes o marcos que posiblemente tengan diferentes fundamentos:
La interfaz pública de un objeto es un contrato. Si su
Array
tipo expone unSort
método, cada matriz a partir de ahora, independientemente del tipo o la versión del lenguaje, debe admitirlo. Sin embargo, si anArray
es solo una representación de datos en la memoria con un contrato delgado y la lógica se mueve a una clase comoArray.Sort
, le brinda más flexibilidad para ampliar o modificar su comportamiento sin romper la compatibilidad.Vea esta cita de Clean Code de Bob Martin :
Los objetos exponen el comportamiento y ocultan datos; La estructura de datos expone los datos y no tiene un comportamiento significativo"
En este caso, la List
clase es una clase que expone el comportamiento: oculta sus datos (se implementa como una matriz redimensionable, pero eso es un detalle de implementación), pero expone la funcionalidad. An int[]
, sin embargo, es una estructura de datos. int
Es un bloque asignado secuencialmente . Tiene una Length
propiedad porque es parte de sus datos , pero no tiene mucho comportamiento: el comportamiento se descarga a una clase que opera en él.
- Podría ser una elección estilística. Python no es un lenguaje puramente OO, y es posible que no tenga la garantía de Java/C# "todo es un objeto", por lo que optó por usar funciones de lenguaje como
len()
esa, independientemente de si su parámetro es una instancia de objeto o no.
Respuesta anterior
La razón de ser de los métodos estáticos en C# es tener métodos vinculados a un tipo pero no a una instancia . Puede haber varias razones para eso:
- Los métodos que pueden operar tanto en una instancia
null
como enstring.IsNullOrEmpty
, naturalmente, no pueden ser métodos de instancia, porque es posible que no haya una instancia. - Los métodos de fábrica que crean una instancia, de manera similar, no tienen una instancia desde la cual llamar. Por ejemplo, el método estático alguien obsoleto
WebRequest.Create()
devuelve una nueva instancia de un tipo derivado deWebRequest
. - Si bien es fácil olvidarlo, .NET tiene un manejo diferente para un tipo integral nativo (
int i =5
) y un tipo de referencia encuadrado a su alrededor (object boxed = 5
). Si llama a un método en un tipo de valor (int i = 5; t.ToString();
), incurre en el costo de encasillarlo. Es por eso que algunas operaciones no se definen como un método sobre un objeto, sino como un método estático que acepta un valor. Es por eso que las operaciones matemáticas comoMath.Abs
no se definen como métodos en los tipos en caja, sino como funciones estáticas que reciben y devuelven valores. - A veces se utilizan métodos estáticos para "configurar" un tipo, no una instancia específica sino el comportamiento de ese tipo en el sistema. Por ejemplo,
[ServicePointManager.SetTcpKeepAlive()][1]
configura el comportamiento de todas las clases que usan la pila de HTTP(s) compartido(s) en .NET, para que pueda configurarlas todas a través de estos métodos estáticos.
Los métodos estáticos tienen mucho sentido
- cuando el método está relacionado con el tipo pero no requiere una instancia existente; o
- cuando el lenguaje admite espacios de nombres solo a través de clases.
C# solo admite funciones como miembros de clases, por lo que las funciones auxiliares deben escribirse como métodos estáticos. La clase C# Math
es un excelente ejemplo.
JavaScript no tiene clases, pero cada variable no local está asociada con algún objeto; en el navegador, se puede acceder a la mayoría de las API a través del window
objeto. Sin embargo, hay métodos a los que se puede acceder a través de un objeto similar a una clase, por ejemplo, Array.isArray(x)
.
Python tiene un diseño idiosincrásico que está principalmente orientado a objetos, pero tiene algunas funciones integradas como len()
o iter()
, en gran parte por razones históricas. Bajo el capó, invocan métodos reales, por ejemplo, iter(x)
es por lo general x.__iter__()
, con un respaldo para objetos que se pueden indexar como una lista.
Más interesantes son los métodos que static
no se deben a las peculiaridades del lenguaje, sino a que tiene sentido. Los métodos similares a constructores o de fábrica son el ejemplo clásico en el que no tenemos un objeto existente al que podamos llamar un método. Algunas funciones tampoco operan exactamente en un objeto, por ejemplo, String.IsNullOrEmpty(x)
opera en cero o un objeto. Esto no funciona como un método, por ejemplo x.IsNullOrEmpty()
, arrojaría una excepción si es nulo. El Array.isArray(x)
método en JS es otro gran ejemplo: queremos llamar isArray(x)
a objetos de cualquier tipo, no solo a matrices. Por lo tanto, un método de instancia no funciona a menos que sea parte del prototipo de cada objeto.