¿No puedo usar todos los métodos estáticos?

64

¿Cuál es la diferencia entre los dos métodos UpdateSubject a continuación? Sentí que usar métodos estáticos es mejor si solo quieres operar en las entidades. ¿En qué situaciones debo ir con métodos no estáticos?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

Sé que recibiré muchas patadas de la comunidad por esta pregunta realmente molesta, pero no pude evitar preguntarla.

¿Esto se vuelve poco práctico cuando se trata de herencia?

Actualización: está
sucediendo en nuestro lugar de trabajo ahora. Estamos trabajando en una aplicación web asp.net de 6 meses con 5 desarrolladores. Nuestro arquitecto decidió que usamos todos los métodos estáticos para todas las API. Su razonamiento es que los métodos estáticos son livianos y beneficia a las aplicaciones web al mantener baja la carga del servidor.

Alejandro
fuente
99
Rich Hickey alentaría algo no idiomático como eso (todos los métodos estáticos). Si le gusta un estilo funcional, también podría usar un lenguaje funcional;) ​​No todo en el software se modela mejor como un objeto.
Trabajo
13
@ Job: Realmente no veo cómo funciona esto, es de procedimiento.
Aaronaught
2
@ Job: esta clase no es inmutable.
Aaronaught
3
@DonalFellows: Por supuesto que puedes, C # y F # son paradigmas mixtos. Pero el hecho es que, métodos estáticos! = Programación funcional. FP significa pasar / encadenar funciones, tipos de datos inmutables, evitar efectos secundarios, etc. Eso es completamente opuesto a lo que hace el fragmento de código en el OP.
Aaronaught
44
"Su razonamiento como métodos estáticos es ligero y beneficia a las aplicaciones web al mantener baja la carga del servidor". Eso está mal. ¿Mantener baja la carga del servidor?
mamcx

Respuestas:

87

Iré con los problemas más obvios de los métodos estáticos con parámetros explícitos "this":

  1. Pierdes el despacho virtual y, posteriormente, el polimorfismo. Nunca puede anular ese método en una clase derivada. Por supuesto, puede declarar un método new( static) en una clase derivada, pero cualquier código que acceda a él debe tener en cuenta toda la jerarquía de la clase y hacer una comprobación y conversión explícitas, que es precisamente lo que se supone que debe evitar OO .

  2. Como una extensión del n. ° 1, no puede reemplazar instancias de la clase con una interfaz , porque las interfaces (en la mayoría de los idiomas) no pueden declarar staticmétodos.

  3. La verbosidad innecesaria. ¿Cuál es más legible: Subject.Update(subject)o simplemente subject.Update()?

  4. Comprobación de argumentos. Una vez más depende del lenguaje, pero muchos compilarán una verificación implícita para garantizar que el thisargumento no sea nullpara evitar que un error de referencia nulo cree condiciones de tiempo de ejecución inseguras (una especie de desbordamiento del búfer). Al no utilizar métodos de instancia, tendría que agregar esta verificación explícitamente al comienzo de cada método.

  5. Es confuso. Cuando un programador normal y razonable ve un staticmétodo, naturalmente asumirá que no requiere una instancia válida (a menos que requiera varias instancias, como un método de comparación o igualdad, o se espera que pueda operar con nullreferencias) . Ver los métodos estáticos utilizados de esta manera nos hará hacer una doble o tal vez una toma triple, y después de la 4ta o 5ta vez estaremos estresados ​​y enojados y Dios te ayudará si conocemos la dirección de tu casa.

  6. Es una forma de duplicación. Lo que realmente sucede cuando invoca un método de instancia es que el compilador o el tiempo de ejecución busca el método en la tabla de métodos del tipo y lo invoca usando thiscomo argumento. Básicamente estás re-implementando lo que el compilador ya hace. Está violando DRY , repitiendo el mismo parámetro una y otra vez en diferentes métodos cuando no es necesario.

Es difícil concebir una buena razón para reemplazar los métodos de instancia con métodos estáticos. Por favor no

Aaronaught
fuente
55
+1 solo para tu línea final. Deseo que mucha más gente piense "método de instancia" antes de pensar "método estático".
Stuart Leyland-Cole
44
+1. Añadiría que los métodos estáticos hacen que sea difícil de escribir pruebas, ya que en la mayoría de los casos no se pueden burlar de ellas: una vez que cualquier parte de su código depende de un método estático, no puede sustituirlo por un no- op en sus pruebas. Un buen ejemplo en el mundo .NET sería clases como File, FileInfoy DirectoryInfo(y de hecho, hay bibliotecas que las ocultan detrás de interfaces que luego pueden inyectarse según sea necesario y burlarse en las pruebas).
sm
Creo que sus puntos sobre el polimorfismo / herencia son discutibles. no se debe usar la herencia para la reutilización del código, por lo que es irrelevante. la parte de herencia también es dudosa. En la mayoría de los idiomas modernos, las firmas de métodos son polimórficas en sí mismas. Si adoptas un enfoque funcional, comienzas a pasar métodos y compones métodos complejos a partir de métodos simples que son intercambiables en función de su interfaz. básicamente no hay inconvenientes para los métodos estáticos que no se compensan simplemente diseñando con ellos en mente, al igual que con OOP.
sara
@sm eso simplemente no es cierto. Si tiene una fuerte dependencia de CUALQUIER COSA, estática o no, que no puede estar en una prueba unitaria, entonces no es comprobable, punto. La estática no causa esto. Se puede inyectar un método estático de la misma manera que una clase que representa una conexión db. está asumiendo que "estático" significa "dependencias estáticas más ocultas que afectan el estado global y los recursos externos". no es justo.
sara
@kai intente inyectar un método estático que haga Y en lugar de X, sean cuales sean X e Y. No necesita depender de nada externo para atornillar. Tal vez solo le interese probar algún aspecto que no requiera un largo cálculo X para realizar. ¿Cómo inyectar su elegante método estático sin crear un contenedor entonces? Pasar funciones no es compatible con todos los idiomas. Los métodos estáticos tienen sus casos de uso y los uso cuando tiene sentido hacerlo . Esto es, como siempre sucede, un caso de "solo porque puedes no necesariamente significa que debas".
sm
13

Has descrito más o menos exactamente cómo haces OOP en C, en el que solo hay métodos estáticos disponibles, y "esto" es un puntero de estructura. Y sí, puede hacer un polimorfismo de tiempo de ejecución en C especificando un puntero de función durante la construcción para almacenarlo como un elemento de la estructura. Los métodos de instancia disponibles en otros idiomas son solo azúcar sintáctico que esencialmente hace exactamente lo mismo bajo el capó. Algunos lenguajes, como python, están a mitad de camino, donde el parámetro "self" se enumera explícitamente, pero la sintaxis especial para llamarlo maneja la herencia y el polimorfismo por usted.

Karl Bielefeldt
fuente
FWIW, con C puedes hacer un despacho virtual de esta manera: obj->type->mthd(obj, ...);y eso es sustancialmente similar a lo que sucede con el despacho virtual en otros lenguajes (pero detrás de escena).
Donal Fellows
@Karl ¿Puede proporcionar algún tipo de referencia escrita para comenzar a cavar más profundo?
Jorge Lavín
Es posible que desee ver GObject como una buena implementación de referencia.
Karl Bielefeldt
10

El problema con el método estático llega en el momento en que necesita una subclase.

Los métodos estáticos no se pueden anular en subclases, por lo tanto, sus nuevas clases no pueden proporcionar nuevas implementaciones de los métodos, lo que los hace menos útiles.


fuente
2
Esto no es necesariamente un problema. Algunos lenguajes proporcionan otros mecanismos (métodos no virtuales en C ++ y C #) para lograr lo mismo intencionalmente. ¡C # incluso proporciona métodos "sellados" para extender aún más esta idea! Obviamente, no harías esto solo por el gusto de hacerlo, pero es una buena técnica para tener en cuenta ...
Shog9
@Steve, eso no está reemplazando, ya que aún puedes llamar a ambos métodos directamente.
@Steve, en Java, los métodos estáticos no se pueden anular.
@ Thorbjørn: en primer lugar, la pregunta no está etiquetada con Java ni con ningún otro lenguaje específico de OOP. Más importante aún, no dije que podría anular una función miembro estática. Estaba tratando de usar una analogía para hacer un punto. Como claramente fallé, se eliminaron los comentarios.
Steve314
2
En cierto sentido, estas siguen siendo "nuevas implementaciones de métodos". Simplemente no en el sentido estándar de OOP. Esto se siente como si dijera "tu redacción tampoco es perfecta, nur-nur-na-nur-nur" ;-)
Steve314
7

Si declara un método static, no tiene que instanciar un objeto de la clase (usando la newpalabra clave) para ejecutar el método. Sin embargo, no puede hacer referencia a ninguna variable miembro a menos que también sean estáticas, en cuyo caso esas variables miembro pertenecen a la clase, no a un objeto instanciado específico de la clase.

Dicho de otra manera, la staticpalabra clave hace que una declaración forme parte de la definición de la clase, por lo que se convierte en un único punto de referencia en todas las instancias de la clase (objetos instanciados de la clase).

Se deduce, entonces, que si desea múltiples copias en ejecución de su clase, y no desea que el estado (variables miembro) se comparta entre todas sus copias en ejecución (es decir, cada objeto tiene su propio estado único), entonces usted no puede declarar esas variables miembro estáticas.

En general, debe usar staticclases y métodos solo cuando está creando métodos de utilidad que aceptan uno o más parámetros, y devuelve un objeto de algún tipo, sin ningún efecto secundario (es decir, cambios en las variables de estado en la clase)

Robert Harvey
fuente
1
Creo que el OP entiende lo que staticsignifica, está preguntando por qué no simplemente declarar todo como estático.
Ed S.
1
Él está pasando en una instancia de Subject- sino como un parámetro explícito, en lugar de lo implícito thisparámetro.
Steve314
Bueno, sí ... pero no entiendo tu punto, supongo.
Ed S.
@Ed: solo que puede hacer prácticamente cualquier cosa a través de un parámetro explícito que podría hacerlo a través de un thisparámetro implícito . Hay diferencias en las expectativas, pero fuera de la herencia, no hay una diferencia real en lo que puede hacer. Por ejemplo, se puede acceder a esos miembros no estáticos , a través del parámetro explícito.
Steve314
Estaba operando bajo el supuesto de que no podía llegar a los miembros privados del argumento en C # incluso a través de un método estático declarado en la misma clase (como puede hacerlo en C ++). No estoy seguro de por qué pensé eso, pero estaba equivocado.
Ed S.
3

La razón por la que las personas argumentan que los "métodos estáticos muestran un mal diseño" no se debe necesariamente a los métodos, en realidad son datos estáticos, implícitos o explícitos. Por implícito quiero decir CUALQUIER estado en el programa que no está contenido en los valores o parámetros de retorno. Modificar el estado en una función estática es un retroceso a la programación de procedimientos. Sin embargo, si la función no modifica el estado, o delega la modificación del estado a las funciones del objeto componente, en realidad es más un paradigma funcional que procesal.

Si no está cambiando de estado, los métodos y clases estáticos pueden tener muchos beneficios. Hace que sea mucho más fácil garantizar que un método sea puro y, por lo tanto, libre de efectos secundarios y cualquier error sea totalmente reproducible. También garantiza que los diferentes métodos en múltiples aplicaciones que usan una biblioteca compartida puedan estar seguros de que obtendrán los mismos resultados con la misma entrada, lo que facilita mucho el seguimiento de errores y las pruebas unitarias.

En última instancia, no hay diferencia en la práctica entre los objetos sobredimensionados que se ven comúnmente, como 'StateManager' y los datos estáticos o globales. El beneficio de los métodos estáticos utilizados correctamente es que pueden indicar la intención del autor de no modificar el estado a revisores posteriores.

Mathieson
fuente
1
Su declaración "... es un retroceso a la programación de procedimientos ..." me hace pensar que lo considera tan malo, supongo que es parte de OO en cierto sentido.
NoPuerto
making ... unit testing much easier.No, no lo hará. Hace que sea imposible probar unitario cualquier código que llame a métodos estáticos , ya que no puede aislarlo del método estático. Una llamada a un método estático se convierte en una dependencia codificada de esa clase.
StuperUser
2

Dependiendo del idioma y de lo que intente hacer, la respuesta puede ser que no hay mucha diferencia, pero la staticversión tiene un poco más de desorden.

Como su sintaxis de ejemplo sugiere Java o C #, creo que Thorbjørn Ravn Andersen tiene razón al señalar el problema primordial (con enlace tardío). Con un método estático, no existe un parámetro "especial" para que se base el enlace tardío, por lo que no puede tener un enlace tardío.

En efecto, un método estático es solo una función en un módulo, ese módulo tiene el mismo nombre que la clase; no es realmente un método OOP en absoluto.

Steve314
fuente
2

Básicamente estás derrotando todo el punto de los objetos cuando haces esto. Hay una buena razón por la que hemos pasado del código de procedimiento al código orientado a objetos.

Yo NUNCA declarar un método estático que tuvo el tipo de clase como un parámetro. Eso solo hace que el código sea más complejo sin ganancia.

Loren Pechtel
fuente
3
Puede hacerlo si pasa un método objecta un staticmétodo y devuelve uno nuevo object. Los programadores funcionales hacen esto todo el tiempo.
Robert Harvey
pregunta: ¿considera la Mathclase estática "compleja sin ganancia", o preferiría que todos los tipos de números tuvieran métodos virtuales que definieran valor absoluto, negación, suma, cuadratura, raíces arbitrarias, etc.? No lo creo. los objetos están limpios cuando necesita almacenar un estado mutable oculto donde necesita imponer algunas invariantes. combinar todos los métodos concebibles que operan en un tipo en el tipo en sí, ESO es algo que conduce a un código complejo e imposible de mantener. Estoy de acuerdo con Robert, es posible que desee ver cómo funcionan los programadores funcionales, ¡puede ser una verdadera revelación!
sara
@kai Como dije, más o menos. La clase de Matemáticas es una excepción razonable, aunque asociarla a los tipos numéricos no sería una mala idea.
Loren Pechtel
2

Aquí hay muchas respuestas geniales, y son mucho más perspicaces y conocimientos que cualquier otra cosa que se me ocurra como respuesta, pero siento que hay algo que no se está abordando:

"Nuestro arquitecto decidió que usamos todos los métodos estáticos para todas las API. Su razonamiento es que los métodos estáticos son livianos y beneficia a las aplicaciones web al mantener baja la carga del servidor ".

(El énfasis es mío)

Mi 2c en esta parte de la pregunta es esta:

Teóricamente, lo que se dice es cierto: llamar a un método estático solo tiene la sobrecarga de invocar ese método. Llamar a un método no estático (instancia) tiene la sobrecarga adicional de instanciar primero el objeto y, en algún momento, destruir la instancia (ya sea manualmente o mediante alguna forma de recolección automática de basura, dependiendo de la plataforma utilizada).

Juega un poco del abogado del diablo en esto: podemos ir más allá y decir cosas como:

  • esto puede llegar a ser realmente malo si se crea una instancia para cada invocación del método (instancia) (en lugar de dejarlo estático y llamarlo así)

  • dependiendo de la complejidad del constructor, la jerarquía de tipos, otros miembros de la instancia y otros factores desconocidos, la sobrecarga adicional de la llamada al método no estático puede variar y llegar a ser realmente grande

En verdad, en mi humilde opinión, los puntos anteriores (y el enfoque general de "usemos las estadísticas porque son más rápidas") son argumentos / evaluaciones de hombre de paja:

  • si el código es bueno y, más específico para este argumento, si las instancias se crean solo cuando es necesario (y se destruyen de manera óptima), entonces no obtiene ningún gasto adicional por usar métodos de seguro cuando sea apropiado (porque si solo cree los objetos necesarios, esos se habrían creado incluso si ese método se declarara como estático en algún otro lugar = misma sobrecarga de instanciación)

  • Al abusar de la declaración de métodos estáticos de esta manera, es posible ocultar algunos problemas con su código con respecto a cómo se crean las instancias (dado que el método es estático, un código de instancia incorrecto puede pasar desapercibido hasta más tarde, y eso nunca es bueno).

Shivan Dragon
fuente
2

Esta es una pregunta muy interesante que tiende a tener respuestas muy diferentes dependiendo de la comunidad a la que se la pregunte. Otras respuestas parecen ser C # o sesgo de Java: un lenguaje de programación que está orientado (principalmente) a objetos puros y tiene una especie de visión idiomática de lo que es la orientación a objetos.

En otras comunidades, como C ++, la orientación a los objetos se interpreta con más liberalidad. Algunos bien saben que los expertos han estudiado el tema y, en realidad, concluyen que las funciones libres mejoran la encapsulación (el énfasis es mío):

Ahora hemos visto que una forma razonable de medir la cantidad de encapsulación en una clase es contar la cantidad de funciones que podrían romperse si cambia la implementación de la clase. Siendo ese el caso, queda claro que una clase con n funciones miembro está más encapsulada que una clase con n + 1 funciones miembro. Y esa observación es lo que justifica mi argumento a favor de preferir funciones que no son miembros y no amigos a funciones miembros

Scott Meyers

Para aquellos que no están familiarizados con la terminología de C ++:

  • función miembro = método
  • función no miembro = método estático
  • no amigo = usar solo API pública
  • no miembro no amigo función = método estático que usa solo API pública

Ahora, para responder tu pregunta:

¿No puedo usar todos los métodos estáticos?

No, no siempre debes usar métodos estáticos.

Pero, debe usarlos siempre que solo necesite usar la API pública de una clase.

authchir
fuente
-3

en Java ...

Los métodos estáticos no se sobrescriben, sino que se ocultan, por lo que sería una gran diferencia (¿problema?) En el caso de extender la clase.

Por otro lado, noté que los programadores de Java tienden a pensar que TODO TIENE que ser un objeto, sin entender realmente por qué, y no estoy de acuerdo.

Estático debe usarse para código específico de la clase. La estática no funciona bien con la herencia.

Algunos buenos ejemplos de uso de métodos estáticos son funciones independientes sin efectos secundarios.

ver también la palabra clave sincronizada ...

Nicolas
fuente
2
¿Cómo se esconde este método estático y dónde surgiría esta diferencia y este problema al extender la clase? ¿Cómo encaja sincronizado con esto? ¿Dónde se encuentran los problemas de estática y herencia? ¿Puede proporcionar algunos métodos estáticos buenos y malos en las bibliotecas principales de Java y explicar por qué cada uno es el caso?