¿Cuál es la longitud ideal de un método para ti? [cerrado]


122

En la programación orientada a objetos, por supuesto, no existe una regla exacta sobre la longitud máxima de un método, pero aún así estas dos citas se contradicen entre sí, por lo que me gustaría escuchar lo que piensas.

En Clean Code: A Handbook of Agile Software Craftsmanship , Robert Martin dice:

La primera regla de funciones es que deben ser pequeñas. La segunda regla de funciones es que deberían ser más pequeñas que eso. Las funciones no deben tener 100 líneas de largo. Las funciones casi nunca deben tener 20 líneas de largo.

y da un ejemplo del código Java que ve de Kent Beck:

Cada función en su programa tenía solo dos, tres o cuatro líneas de largo. Cada uno era transparentemente obvio. Cada uno contaba una historia. Y cada uno te llevó al siguiente en un orden convincente. ¡Así de cortas deberían ser tus funciones!

Esto suena genial, pero por otro lado, en Code Complete , Steve McConnell dice algo muy diferente:

Se debe permitir que la rutina crezca orgánicamente hasta 100-200 líneas, décadas de evidencia dicen que las rutinas de tal longitud no son más propensas a errores que las rutinas más cortas.

Y da una referencia a un estudio que dice que las rutinas de 65 líneas o más son más baratas de desarrollar.

Entonces, si bien hay opiniones divergentes sobre el asunto, ¿hay una práctica recomendada funcional para usted?


17
Las funciones deben ser fáciles de entender. La longitud debe seguir a partir de eso, dependiendo de las circunstancias.
Henk Holterman

56
Creo que el límite real es de 53 líneas. Con un tamaño de línea promedio de 32.4 caracteres. En serio, no hay una respuesta definitiva. Un método de 100 líneas puede ser muy claro y fácil de mantener, y un método de 4 líneas puede ser una pesadilla de entender. Sin embargo, en general, los métodos largos tienden a tener demasiadas responsabilidades y son más difíciles de comprender y mantener que los más pequeños. Pensaría en términos de responsabilidades y trataría de tener una sola responsabilidad por método.

23
Hay un término en la programación llamado "coherencia funcional". Se debe permitir que varíe la longitud de una función siempre que su implementación todavía constituya una única unidad lógica coherente en su aplicación. Es más probable que la división arbitraria de funciones para hacerlas más pequeñas hinche su código y perjudique la mantenibilidad.

99
Y, si desea limitar la complejidad de sus funciones, debe medir su complejidad ciclomática , no su longitud. Una switchdeclaración con 100 casecondiciones es más fácil de mantener que 10 niveles de ifdeclaraciones anidadas entre sí.

10
El enfoque de Bob Martin es de 2008, el de Steve Mc Connell de 1993. Tienen diferentes filosofías sobre lo que es el "buen código", y en mi humilde opinión, Bob Martin intenta adquirir un nivel mucho más alto de calidad de código.
Doc Brown

Respuestas:


115

Las funciones normalmente deberían ser cortas, entre 5-15 líneas es mi "regla de oro" personal al codificar en Java o C #. Este es un buen tamaño por varias razones:

  • Se adapta fácilmente a su pantalla sin desplazarse
  • Se trata del tamaño conceptual que puedes tener en tu cabeza
  • Es lo suficientemente significativo como para requerir una función por derecho propio (como un trozo de lógica independiente y significativo)
  • Una función de menos de 5 líneas es un indicio de que quizás esté dividiendo demasiado el código (lo que hace que sea más difícil de leer / comprender si necesita navegar entre las funciones). ¡O eso o estás olvidando tus casos especiales / manejo de errores!

Pero no creo que sea útil establecer una regla absoluta, ya que siempre habrá excepciones / razones válidas para divergir de la regla:

  • Una función de acceso de una línea que realiza una conversión de tipo es claramente aceptable en algunas situaciones.
  • Hay algunas funciones muy cortas pero útiles (p. Ej., Intercambio según lo mencionado por el usuario desconocido) que claramente necesitan menos de 5 líneas. No es gran cosa, unas pocas funciones de 3 líneas no hacen ningún daño a su base de código.
  • Una función de 100 líneas que es una sola declaración de cambio grande podría ser aceptable si es extremadamente claro lo que se está haciendo. Este código puede ser conceptualmente muy simple incluso si requiere muchas líneas para describir los diferentes casos. A veces se sugiere que esto se refactorice en clases separadas y se implemente usando herencia / polimorfismo, pero en mi humilde opinión, esto está llevando a OOP demasiado lejos: prefiero tener solo una gran declaración de cambio de 40 vías que 40 nuevas clases con las que lidiar, además a una declaración de cambio de 40 vías para crearlos.
  • Una función compleja podría tener muchas variables de estado que se volverían muy desordenadas si se pasaran entre diferentes funciones como parámetros. En este caso, podría razonablemente argumentar que el código es más simple y fácil de seguir si mantiene todo en una sola función grande (aunque, como Mark señala correctamente, esto también podría ser un candidato para convertirse en una clase para encapsular tanto la lógica y estado).
  • A veces, las funciones más pequeñas o más grandes tienen ventajas de rendimiento (quizás debido a razones de alineación o JIT como menciona Frank). Esto depende en gran medida de la implementación, pero puede marcar la diferencia: ¡asegúrese de hacer una referencia!

Básicamente, use el sentido común , manténgase en tamaños de función pequeños en la mayoría de los casos, pero no sea dogmático al respecto si tiene una buena razón para hacer una función inusualmente grande.


99
También hay una razón técnica / de rendimiento para métodos cortos: hits de caché JIT. Es más probable que muchos métodos más pequeños y reutilizables se hayan llamado antes. Ah, y un beneficio adicional de diagnóstico, StackTraces está más centrado en la lógica que se hizo popular.
Luke Puplett el

20
"usar el sentido común" es el consejo más importante
Simon

Se debe tener en cuenta que esta heurística dará como resultado quejas de "código de ravioles" debido a la profundidad de la pila en los puntos de interrupción, al depurar.
Frank Hileman

55
@FrankHileman Tomaré ravioles sobre espagueti cualquier día cuando escriba código

1
@Snowman: estas opciones no son mutuamente excluyentes ... profundidades de pila cortas sin espagueti es lo ideal. ¿Profundas profundidades de pila, con espagueti a un lado?
Frank Hileman

30

Si bien estoy de acuerdo con los comentarios de otros cuando dijeron que no hay una regla estricta sobre el número LOC correcto, apuesto a que si miramos hacia atrás en los proyectos que hemos visto en el pasado e identificamos todas las funciones anteriores, digamos 150 líneas de código, yo ' Supongo que llegaríamos a un consenso de que 9 de cada 10 de esas funciones rompen el SRP (y muy probablemente también el OCP), tienen demasiadas variables locales, demasiado flujo de control y generalmente son difíciles de leer y mantener.

Entonces, si bien LOC puede no ser un indicador directo de código incorrecto, ciertamente es un indicador indirecto decente de que cierta función podría escribirse mejor.

En mi equipo caí en la posición de líder y, por alguna razón, la gente parece estar escuchándome. Lo que generalmente decidí es decirle al equipo que, si bien no hay un límite absoluto, cualquier función con más de 50 líneas de código debería, como mínimo, levantar una bandera roja durante la revisión del código, para que lo revisemos y reevaluémoslo por complejidad y violaciones de SRP / OCP. Después de ese segundo vistazo, podríamos dejarlo solo o podríamos cambiarlo, pero al menos hace que la gente piense en estas cosas.


3
Esto parece razonable: LOC no significa nada con respecto a la complejidad o la calidad del código, pero puede ser un buen marcador de la posibilidad de que las cosas se refactoricen.
cori

44
"llegar a un consenso de que 9 de cada 10 de esas funciones rompen el SRP" - No estoy de acuerdo, estoy bastante seguro de que 10 de cada 10 de esas funciones lo romperán ;-)
Doc Brown

1
+1 para levantar una bandera durante la revisión del código: en otras palabras, no es una regla dura y rápida, pero analicemos este código como un grupo.

22

Entré en un proyecto que no se ha preocupado por las pautas de codificación. Cuando miro el código, a veces encuentro clases con más de 6000 líneas de código y menos de 10 métodos. Este es un escenario de terror cuando tienes que corregir errores.

Una regla general de cuán grande debe ser un método al máximo a veces no es tan buena. Me gusta la regla de Robert C. Martin (tío Bob): "Los métodos deben ser pequeños, más pequeños que pequeños". Intento usar esta regla todo el tiempo. Estoy tratando de mantener mis métodos simples y pequeños aclarando que mi método solo hace una cosa y nada más.


44
No veo por qué una función de 6000 líneas es más difícil de depurar que una más corta ... A menos que también tenga otros problemas (por ejemplo, repetición)
Calmarius

17
No tiene nada que ver con la depuración ... se trata de complejidad y facilidad de mantenimiento. ¿Cómo extenderías o cambiarías un método de 6000 líneas hecho por otro?
Smokefoot

10
@Calmarius, la diferencia suele ser que las funciones de 6000 líneas tienden a contener variables locales que se declararon muy lejanas (visualmente), lo que dificulta al programador desarrollar el contexto mental requerido para tener una alta confianza sobre el código. ¿Puede estar seguro de cómo se inicializa y construye una variable en un punto dado? ¿Está seguro de que nada va a alterar su variable después de configurarla en la línea 3879? Por otro lado, con 15 métodos de línea, puede estar seguro.
Daniel B

8
@Calmarius estuvo de acuerdo, pero ambas declaraciones son un argumento contra 6000 funciones LOC.
Daniel B

2
Una variable local en un método de 600 líneas es esencialmente una variable global.
MK01

10

No se trata del número de líneas, se trata de SRP. De acuerdo con este principio, su método debe hacer una y solo una cosa.

Si su método hace esto Y esto Y esto O eso => ​​probablemente esté haciendo demasiado. Intente ver este método y analizar: "aquí obtengo estos datos, los ordeno y obtengo los elementos que necesito" y "aquí proceso estos elementos" y "aquí finalmente los combino para obtener el resultado". Estos "bloques" deben ser refactorizados a otros métodos.

Si simplemente sigue SRP, la mayoría de su método será pequeño y con clara intención.

No es correcto decir "este método es> 20 líneas, por lo que está mal". Puede ser una indicación de que algo puede estar mal con este método, no más.

Puede tener un cambio de 400 líneas en un método (a menudo sucede en telecomunicaciones), y todavía es responsabilidad única y está perfectamente bien.


2
Grandes declaraciones de cambio, formato de salida, definiciones de hashes / diccionarios que deberían codificarse en lugar de ser flexibles en alguna base de datos, esto a menudo sucede y está perfectamente bien. Mientras la lógica esté dividida, entonces todo está bien. Un método grande podría incitarlo a pensar 'debería dividir esto'. La respuesta podría ser 'no, esto está bien como está' (o sí, esto es un desastre absoluto)
Martijn

¿Qué significa "SRP"?
thomthom

3
SRP significa Principio de responsabilidad única y establece que cada clase (o método) debe tener una sola responsabilidad. Está relacionado con la cohesión y el acoplamiento. Si sigues el SRP, tus clases (o métodos) tendrán una buena cohesión, pero el acoplamiento puede aumentar porque terminas con más clases (o métodos).
Kristian Duske

+1 para SRP. Al escribir funciones cohesivas, uno puede combinarlas más fácilmente en un estilo funcional para lograr resultados más complejos. Al final, es mejor que una función esté compuesta por otras tres funciones que se unen entre sí que tener una sola función que realice tres cosas discretas, incluso si de alguna manera están relacionadas.
Mario T. Lanza

Sí, pero lo que es una responsabilidad única. Es solo un concepto creado en tu cabeza. Si necesita 400 líneas para una sola responsabilidad, su concepto de responsabilidad única es probablemente muy diferente al mío
Xitcod13

9

Depende , en serio, realmente no hay una respuesta sólida a esta pregunta porque el lenguaje con el que está trabajando importa, las líneas cinco a quince mencionadas en esta respuesta podrían funcionar para C # o Java, pero en otros idiomas no da Eres mucho para trabajar. Del mismo modo, dependiendo del dominio en el que esté trabajando, puede encontrarse escribiendo valores de configuración de código en una estructura de datos grande. Con algunas estructuras de datos, es posible que tenga decenas de elementos que necesita establecer, ¿debería dividir las cosas en funciones separadas solo porque su función se ejecuta durante mucho tiempo?

Como otros han señalado, la mejor regla general es que una función debe ser una sola entidad lógica que maneje una sola tarea. Si intenta imponer reglas draconianas que dicen que las funciones no pueden ser más largas que n líneas y hace que ese valor sea demasiado pequeño, su código será más difícil de leer a medida que los desarrolladores intenten usar trucos sofisticados para sortear la regla. Del mismo modo, si lo configura demasiado alto, no será un problema y puede generar un código incorrecto a pesar de la pereza. Su mejor opción es realizar revisiones de código para asegurarse de que las funciones manejan una sola tarea y dejarla así.


8

Creo que un problema aquí es que la longitud de una función no dice nada sobre su complejidad. LOC (líneas de código) son un mal instrumento para medir cualquier cosa.

Un método no debe ser demasiado complejo, pero hay escenarios en los que se puede mantener fácilmente un método largo. Tenga en cuenta que el siguiente ejemplo no dice que no podría dividirse en métodos, solo que los métodos no cambiarían la capacidad de mantenimiento.

por ejemplo, un controlador para datos entrantes puede tener una declaración de cambio grande y luego un código simple por caso. Tengo ese código: administrar los datos entrantes de un feed. 70 (!) Controladores codificados numéricamente. Ahora, uno dirá "usar constantes" - sí, excepto que la API no las proporciona y me gusta estar cerca de "fuente" aquí. Métodos? Claro, lamentablemente todos tratan datos de las mismas 2 grandes estructuras. No hay beneficio en dividirlos, excepto tal vez tener más métodos (legibilidad). El código no es intrínsecamente complejo: un cambio, dependiendo de un campo. Luego, cada caso tiene un bloque que analiza x elementos de datos y los publica. Sin pesadillas de mantenimiento. Hay una condición "if repetida" que determina si un campo tiene datos (pField = pFields [x], if pField-> IsSet () {blabla}) - lo mismo para cada campo.

Reemplace eso con una rutina mucho más pequeña que contenga un bucle anidado y muchas instrucciones de cambio reales y un método enorme puede ser más fácil de mantener que uno más pequeño.

Entonces, lo siento, LOC no es una buena medida para empezar. En todo caso, se deben utilizar los puntos de complejidad / decisión.


1
Los LOC son una buena herramienta para usar en el área en la que proporcionan una medida relevante: proyectos muy grandes en los que se pueden usar para ayudar a dar estimaciones de cuánto tiempo puede llevar completar un proyecto similar. Más allá de eso, la gente tiende a preocuparse demasiado por ellos.
rjzii

Derecha. No es como si el LOC no dependiera a veces de cuán expresivo escribiera el código, sobre requisitos de formato, etc. Solamente. Usted es libre de colocarse en la lista de personas que no entienden por qué la LOC es una mala medida, pero claramente eso no lo hará ver como alguien a quien escuchar.
TomTom

Revise lo que dije nuevamente, noté que el LOC es una buena herramienta solo para un área de medición y uso (es decir, proyectos extremadamente grandes donde se pueden usar para la estimación). Algo más pequeño que la gran escala y utilizan la mayoría, si no todo, el valor de algo más allá de los sonidos rápidos en las reuniones para mantener a la gente feliz. Son como tratar de usar años luz para medir qué tan justa es la cafetería de la oficina, seguro que puedes hacerlo, pero la medida es inútil. Pero cuando necesitas discutir distancias entre estrellas, funcionan muy bien.
rjzii

2
+1 una función debe tener todo el código que necesita para realizar la función. Debería hacer una cosa y solo una cosa, pero si eso requiere 1000 líneas de código, entonces que así sea.
James Anderson el

He escrito controladores para datos de socket entrantes y sí, pueden requerir mil o más LOC. Sin embargo, puedo contar por un lado la cantidad de veces que he necesitado hacerlo y no puedo contar la cantidad de veces que no fue la forma adecuada de codificar.

7

Solo agregaré otra cita.

Los programas deben estar escritos para que la gente los lea, y solo de forma incidental para que las máquinas los ejecuten

- Harold Abelson

Es muy improbable que las funciones que crecen hasta 100-200 sigan esta regla.


1
Excepto cuando contienen un interruptor.
Calmarius

1
o construir un objeto basándose en los resultados de una consulta de base de datos que devuelve decenas de campos por fila del conjunto de resultados ...
jwenting

Los resultados de la base de datos son sin duda una excepción aceptable, además de que generalmente son declaraciones "tontas" que pueblan alguna instancia de una clase (o lo que sea), en lugar de la lógica que debe seguirse.
MetalMikester

6

He estado en esta locura, de una forma u otra, desde 1970.

En todo ese tiempo, con dos excepciones a las que llegaré en un momento, NUNCA he visto una "rutina" bien diseñada (método, procedimiento, función, subrutina, lo que sea) que NECESITA ser más de una página impresa ( unas 60 líneas) de largo. La gran mayoría de ellos eran bastante cortos, del orden de 10-20 líneas.

Sin embargo, he visto MUCHO código de "flujo de conciencia", escrito por personas que aparentemente nunca oyeron hablar de la modularización.

Las dos excepciones fueron casos muy especiales. Uno es en realidad una clase de casos de excepción, que agrupo: grandes autómatas de estado finito, implementados como grandes declaraciones de cambio feo, generalmente porque no hay una forma más limpia de implementarlos. Estas cosas generalmente aparecen en equipos de prueba automatizados, analizando registros de datos del dispositivo bajo prueba.

La otra era la rutina de torpedos de fotones del juego STARTRK Matuszek-Reynolds-McGehearty-Cohen, escrito en CDC 6600 FORTRAN IV. Tenía que analizar la línea de comando, luego simular el vuelo de cada torpedo, con perturbaciones, verificar la interacción entre el torpedo y cada tipo de cosa que podría golpear, y, por cierto, simular la recursividad para hacer conectividad de 8 vías en cadenas de novas de torpedear una estrella que estaba al lado de otras estrellas.


2
+1 por el ambiente de "salir de mi césped" que recibo de esta respuesta. Además, desde la experiencia personal de antes, los idiomas OOP estaban muy extendidos.

No es tanto "salir de mi césped" como una observación de que he visto MUCHO código basura a lo largo de los años, y parece que se está poniendo PEOR.
John R. Strohm

Mi jefe tiene la costumbre de escribir métodos de varios cientos de líneas, a menudo con varios niveles de ifs anidados. También usa clases parciales (.NET) para "dividir" una clase en varios archivos para poder afirmar que los mantiene cortos. Esas son solo dos cosas con las que tengo que lidiar. He estado haciendo esto durante unos 25 años, y puedo confirmar que las cosas ESTÁN empeorando. Y ahora es tiempo de que regrese a ese desastre.
MetalMikester

5

Si encuentro un método largo, podría apostar que este método no se prueba adecuadamente en la unidad o la mayoría de las veces no tiene una prueba unitaria. Si comienza a hacer TDD, nunca construirá métodos de 100 líneas con 25 responsabilidades diferentes y 5 bucles anidados. Las pruebas te obligan a refactorizar constantemente tu desorden y escribir el código limpio del tío Bob.


2

No hay reglas absolutas sobre la longitud del método, pero las siguientes reglas han sido útiles:

  1. El propósito principal de la función es encontrar el valor de retorno. No hay otra razón para su existencia. Una vez que se cumple esa razón, no se debe insertar ningún otro código. Esto necesariamente mantiene las funciones pequeñas. Las llamadas a otras funciones solo deben realizarse si facilita la búsqueda del valor de retorno.
  2. Por otro lado, las interfaces deben ser pequeñas. Esto significa que tiene una gran cantidad de clases, o tiene grandes funciones: una de las dos sucederá una vez que comience a tener suficiente código para hacer algo significativo. Los grandes programas pueden tener ambos.

1
¿Qué pasa con los efectos secundarios: escribir en un archivo, restablecer el estado, etc.?
Vorac

2

¿Los autores quieren decir lo mismo con "función" y "rutina"? Por lo general, cuando digo "función" me refiero a una subrutina / operación que devuelve un valor y un "procedimiento" para uno que no lo hace (y cuya llamada se convierte en una sola declaración). Esta no es una distinción común en todo el SE en el mundo real, pero la he visto en textos de usuario.

De cualquier manera, no hay una respuesta correcta para esto. La preferencia por uno u otro (si es que existe alguna preferencia) es algo que esperaría que fuera muy diferente entre idiomas, proyectos y organizaciones; tal como es con todas las convenciones de código.

El único bit que agregaría es que toda la afirmación "las operaciones largas no son más propensas a errores que las operaciones cortas" no es estrictamente cierta. Además del hecho de que más código equivale a más espacio de error potencial, es muy obvio que dividir el código en segmentos hará que los errores sean más fáciles de evitar y más fáciles de localizar. De lo contrario, no habría razón para romper el código en pedazos, salvo la repetición. Pero esto tal vez sea cierto solo si dichos segmentos están documentados lo suficientemente bien como para que pueda determinar los resultados de una llamada de operación sin leer o rastrear el código real (diseño por contrato basado en especificaciones en lugar de dependencia concreta entre áreas de código).

Además, si desea que las operaciones más largas funcionen bien, es posible que desee adoptar convenciones de código más estrictas para admitirlas. Lanzar una declaración de devolución en el medio de una operación puede estar bien para una operación corta, pero en operaciones más largas esto puede crear una gran sección de código que es condicional pero no obviamente condicional en una lectura rápida (solo por un ejemplo).

Entonces, creo que qué estilo es menos probable que sea una pesadilla llena de errores dependerá en gran medida de las convenciones a las que se adhiera para el resto de su código. :)


1

En mi humilde opinión, no debería tener que usar la barra de desplazamiento para leer su función. Tan pronto como necesite mover la barra de desplazamiento, le tomará más tiempo entender cómo funciona la función.

En consecuencia, depende del entorno de programación habitual del trabajo de su equipo (resolución de pantalla, editor, tamaño de fuente, etc.). En los años 80, tenía 25 líneas y 80 columnas. Ahora, en mi editor, muestro casi 50 líneas. El número de columnas que visualizo no cambió ya que dividí mi pantalla en dos para mostrar dos archivos a veces.

En resumen, depende de la configuración de sus compañeros de trabajo.


2
¿No fueron más bien 24 líneas esa vez? Estoy pensando en terminales 3270 o 9750, donde el 25 era la línea de estado. Y las emulaciones de terminal siguieron esto.
ott--

Algunos sistemas / editores tenían 40 o 50 líneas desde el principio. En la actualidad, 150 líneas no son infrecuentes y más de 200 son factibles, por lo que no es una buena métrica.
Más el

Uso mi pantalla en orientación vertical, puedo ver 200 líneas de código a la vez.
Calmarius

y si no uso ningún salto de línea para dividir mis líneas, puedo codificar un método de 5000 líneas en una sola línea ...
partir del

1

Creo que la respuesta de TomTom se acercó a cómo me siento al respecto.

Cada vez me encuentro más complejo ciclomático en lugar de líneas.

Normalmente apunto a no más de una estructura de control por método, con la excepción de los bucles necesarios para manejar una matriz multidimensional.

A veces me encuentro colocando ifs de una línea en casos de cambio porque, por alguna razón, estos tienden a ser casos en los que dividirlo obstaculiza en lugar de ayudar.

Tenga en cuenta que no cuento la lógica de guardia contra este límite.


Se ha demostrado que la complejidad ciclomática, en grandes cantidades de código de producción real, está MUY fuertemente correlacionada con SLOC sin procesar, lo que hace que el cálculo de la complejidad ciclomática sea una pérdida total de tiempo, energía y ciclos de reloj.
John R. Strohm

@ JohnR.Strohm estoy hablando por método, no en general. Claro, en general, está altamente correlacionado: la pregunta es cómo dividir ese código en métodos. 10 métodos de 100 líneas o 100 métodos de 10 líneas seguirán teniendo el mismo SLOC y complejidad generales, pero será mucho más difícil trabajar con el primero.
Loren Pechtel

yo también. El estudio de correlación examinó MUCHOS códigos y MUCHAS rutinas. (Fue uno de los grandes repositorios públicos.)
John R. Strohm

-3

En OOP todas las cosas se oponen y hay estas características:

  1. Polimorfismo
  2. Abstracción
  3. Herencia

Cuando observa estas reglas, sus métodos generalmente son pequeños, pero no existe ninguna regla para reglas pequeñas o muy pequeñas (por ejemplo, 2-3 líneas). Un beneficio del método pequeño (unidad pequeña, por ejemplo, método o función) son:

  1. mejor legible
  2. mantener mejor
  3. error corregido mejor
  4. cambia mejor
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.