Entiendo la importancia de un código bien documentado. Pero también entiendo la importancia del código autodocumentado . Cuanto más fácil sea leer visualmente una función en particular, más rápido podremos avanzar durante el mantenimiento del software.
Dicho esto, me gusta separar las funciones grandes en otras más pequeñas. Pero lo hago hasta el punto en que una clase puede tener más de cinco de ellos solo para servir un método público. Ahora multiplique cinco métodos privados por cinco públicos, y obtendrá alrededor de veinticinco métodos ocultos que probablemente serán llamados solo una vez por esos públicos.
Claro, ahora es más fácil leer esos métodos públicos, pero no puedo evitar pensar que tener demasiadas funciones es una mala práctica.
[Editar]
La gente me ha preguntado por qué creo que tener demasiadas funciones es una mala práctica.
La respuesta simple: es un presentimiento.
Mi creencia no está, ni por un momento, respaldada por ninguna hora de experiencia en ingeniería de software. Es solo una incertidumbre que me dio un "bloqueo del escritor", pero para un programador.
En el pasado, solo he estado programando proyectos personales. Hace poco me mudé a proyectos basados en equipo. Ahora, quiero asegurarme de que otros puedan leer y comprender mi código.
No estaba seguro de qué mejorará la legibilidad. Por un lado, estaba pensando en separar una función grande en otras más pequeñas con nombres inteligibles. Pero había otro lado de mí que decía que era redundante.
Entonces, estoy pidiendo esto para iluminarme a fin de elegir el camino correcto.
[Editar]
A continuación, incluí dos versiones de cómo podría resolver mi problema. El primero lo resuelve al no separar grandes fragmentos de código. El segundo hace cosas separadas.
Primera versión:
public static int Main()
{
// Displays the menu.
Console.WriteLine("Pick your option");
Console.Writeline("[1] Input and display a polynomial");
Console.WriteLine("[2] Add two polynomials");
Console.WriteLine("[3] Subtract two polynomials");
Console.WriteLine("[4] Differentiate two polynomials");
Console.WriteLine("[0] Quit");
}
Segunda versión:
public static int Main()
{
DisplayMenu();
}
private static void DisplayMenu()
{
Console.WriteLine("Pick your option");
Console.Writeline("[1] Input and display a polynomial");
Console.WriteLine("[2] Add two polynomials");
Console.WriteLine("[3] Subtract two polynomials");
Console.WriteLine("[4] Differentiate two polynomials");
Console.WriteLine("[0] Quit");
}
En los ejemplos anteriores, este último llama a una función que solo se utilizará una vez durante todo el tiempo de ejecución del programa.
Nota: el código anterior está generalizado, pero es de la misma naturaleza que mi problema.
Ahora, aquí está mi pregunta: ¿cuál? ¿Elijo el primero o el segundo?
Respuestas:
Esto es lo que se conoce como encapsulación , que crea una abstracción de control en el nivel superior. Ésto es una cosa buena.
Esto significa que cualquiera que lea el código, cuando llegan a la
startTheEngine()
método en su código, puede ignorar todos los detalles de nivel inferior, tales comoopenIgnitionModule()
,turnDistributorMotor()
,sendSparksToSparkPlugs()
,injectFuelIntoCylinders()
,activateStarterSolenoid()
, y todos los otros complejos, pequeños, las funciones que se deben ejecutar con el fin de facilitar la función mucho más grande y más abstracta destartTheEngine()
.A menos que el problema con el que esté lidiando en su código se relacione directamente con uno de esos componentes, los mantenedores de código pueden seguir adelante, ignorando la funcionalidad encapsulada y de espacio aislado.
Esto también tiene la ventaja adicional de hacer que su código sea más fácil de probar. . Por ejemplo, puedo escribir un caso de
turnAlternatorMotor(int revolutionRate)
prueba y probar su funcionalidad completamente independiente de los otros sistemas. Si hay un problema con esa función y la salida no es lo que esperaba, entonces sé cuál es el problema.El código que no se divide en componentes es mucho más difícil de probar. De repente, sus mantenedores solo están mirando la abstracción en lugar de poder profundizar en componentes medibles.
Mi consejo es seguir haciendo lo que está haciendo, ya que su código se escalará, será fácil de mantener y podrá usarse y actualizarse en los próximos años.
fuente
myString.replaceAll(pattern, originalData);
¿o pegar todo el método replaceAll en mi código, incluida la matriz de caracteres de respaldo que representa el objeto de cadena subyacente cada vez que necesito usar la funcionalidad de una cadena?Si y no. El principio fundamental es: un método debe hacer una cosa y solo una cosa
Es correcto "dividir" su método en métodos más pequeños. Por otra parte, esos métodos deberían ser lo suficientemente generales como para no solo servir ese método "grande" sino ser reutilizables en otros lugares. Sin embargo, en algunos casos, un método seguirá una estructura de árbol simple, como un Método Initialize que llama InitializePlugins, InitializeInterface, etc.
Si tiene un método / clase realmente grande, generalmente es una señal de que está haciendo demasiado y necesita refactorizar para dividir el "blob". Los perphaps ocultan cierta complejidad en otra clase bajo una abstracción, y usan inyección de dependencia
fuente
Creo que, en general, es mejor errar del lado de demasiadas funciones en lugar de muy pocas. Las dos excepciones a esa regla que he visto en la práctica son para los principios DRY y YAGNI . Si tiene muchas funciones casi idénticas, debe combinarlas y usar parámetros para evitar repetirse. También puede tener demasiadas funciones si las creó "por si acaso" y no se están utilizando. No veo absolutamente nada de malo en tener una función que solo se usa una vez si agrega legibilidad y facilidad de mantenimiento.
fuente
La gran respuesta de jmort253 me inspiró a seguir con una respuesta de "sí, pero" ...
Tener muchos métodos privados pequeños no es algo malo, pero presta atención a esa sensación molesta que te hace publicar una pregunta aquí :). Si llega a un punto en el que escribe pruebas que se centran solo en métodos privados (ya sea llamándolos directamente o configurando un escenario tal que se llame de una manera determinada para que pueda probar el resultado) deberías dar un paso atrás.
Lo que puede tener es una nueva clase con su propia interfaz pública más enfocada que intenta escapar. En ese punto, es posible que desee pensar en extraer una clase para encapsular esa parte de la funcionalidad de sus clases existentes que actualmente vive en un método privado. Ahora su clase original puede usar su nueva clase como una dependencia, y puede escribir pruebas más enfocadas directamente en esa clase.
fuente
Casi todos los proyectos de OO a los que me he unido sufrieron mucho por las clases que eran demasiado grandes y los métodos que eran demasiado largos.
Cuando siento la necesidad de un comentario que explique la siguiente sección de código, entonces extraigo esa sección en un método separado. A la larga, esto acorta el código. Esos pequeños métodos se reutilizan más de lo que cabría esperar inicialmente. Empiezas a ver oportunidades para reunir un montón de ellos en una clase separada. Pronto estará pensando en su problema a un nivel superior y todo el esfuerzo será mucho más rápido. Las nuevas características son más fáciles de escribir, porque puedes construir sobre esas pequeñas clases que creaste a partir de esos pequeños métodos.
fuente
Una clase con 5 métodos públicos y 25 métodos privados no me parece tan grande. Solo asegúrese de que sus clases tengan responsabilidades bien definidas y no se preocupe demasiado por la cantidad de métodos.
Dicho esto, esos métodos privados deberían centrarse en un aspecto específico de toda la tarea, pero no de manera "doSomethingPart1", "doSomethingPart2". No ganas nada si esos métodos privados son solo piezas de una larga historia dividida arbitrariamente, cada una sin sentido fuera de todo el contexto.
fuente
Extracto del Código Limpio de R. Martin (p. 176):
fuente
Algunas opciones:
Esta bien. Simplemente nombra los métodos privados, así como también los nombres de tus métodos públicos. Y nombra bien tus métodos públicos.
Resuma algunos de estos métodos auxiliares en clases auxiliares o módulos auxiliares. Y asegúrese de que estas clases / módulos auxiliares estén bien nombrados y sean abstracciones sólidas en sí mismas.
fuente
No creo que haya un problema de "demasiados métodos privados" si se siente así, probablemente sea un síntoma de una arquitectura de código deficiente.
Así es como lo pienso ... Si la arquitectura está bien diseñada, generalmente es obvio dónde debería estar el código que hace X. Es aceptable si hay un par de excepciones a esto, pero estas deben ser realmente excepcionales, de lo contrario, refactorice la arquitectura para manejarlas como estándar.
Si el problema es que al abrir su clase, está lleno de métodos privados que están dividiendo fragmentos más grandes en fragmentos de código más pequeños, entonces quizás este sea un síntoma de falta de arquitectura. En lugar de clases y arquitectura, esta área de código se ha implementado de manera procesal dentro de una clase.
Encontrar un equilibrio aquí depende del proyecto y sus requisitos. El argumento contrario a esto es el dicho de que un programador junior puede activar la solución en 50 líneas de código que toma un arquitecto 50 clases.
fuente
Si.
En Python, el concepto de "privado" (como lo usan C ++, Java y C #) realmente no existe.
Hay una convención de nomenclatura "privada", pero eso es todo.
Privado puede conducir a un código difícil de probar. También puede romper el "Principio de Abierto-Cerrado" al hacer que el código simplemente se cierre.
En consecuencia, para las personas que han usado Python, las funciones privadas no tienen valor. Simplemente haga todo público y termine con esto.
fuente