Identificación de Co-Smells en C-Sharp y Soluciones Prácticas
Clase 6 de 14 • Curso de Buenas Prácticas y Código Limpio en C#
Contenido del curso
Clase 6 de 14 • Curso de Buenas Prácticas y Código Limpio en C#
Contenido del curso
Carlos Mauricio Moreno Aguilera
Miguel Teheran
Jorge Reyes Vargas
The Gold Experience 25
David Elias Gatica Morales
Sebastian Jaramillo
wilking trinidad
Salvador Galliano
Lizzeth Alina Neyra Herrera
Abel Sanclemente Sieso
Mario Alejandro Leyva Gil
Alejandro Mora
Miguel Teheran
Jhon F. Cervantes Charris
Donald Miguel Jacamo Estrada
William Antonio Mejía Hernandez
Miguel Teheran
Jesus Maria Gonzalez Guardo
Donovan Villanueva
Marta Maleyka Magallón Escobar
Leandro Cosme Tomassini
Francisco Javier Pineda Giraldo
Raul Lopez
Luis Alvarez
Code Smell, "Demasiados parametros" Creo que se puede resolver pasando una clase o dos como parametro algo como esto:
var numerosSumados = new ValoresNumericos { primerValor = 20, segundoValor = 20 } var opcionesCalculoSuma = new opcionesDeFormato { cantidadDecimales = 2, tituloSalidaTerminal = "Total de suma 2 parametros", tipoDeOperacion = OperacionAritmetica.Suma } public double Calculation( VarloresNumericos numerosCalculo, opcionesDeFormato opcionesFormatoCalculo) . . .
creo que hay otras formas mas elegantes de escribir ese codigo, lo importante es que me parece que las funciones con menos de 4 parametros son mas faciles de leer.
¿Que opinan?
Usar DTOs es una buena estrategia para mejorar la forma en la que se transfieren datos y disminuir el numero de parámetros
Para evaluar code smells, usé mucho Sonarqube, como herramienta de análisis estático. Las reglas para detectar code smells (incluso otras reglas que veremos mas adelante) me ayudaron a entender y mejorar mucho la programación, vale la pena revisarlas e ir entendiéndolas. https://rules.sonarsource.com/csharp/type/Code%20Smell
Lo hice de esta forma, ya que usar if en un programa grande es mas optimo usar switch y ademas si la comparacion es de solo igual a, lo mejor es usar switch <code>
TaskList = new List<string>(); int menuselected = 0; do { menuselected = ShowMainMenu(); switch (menuselected) { case 1: ShowMenuAdd(); break; case 2: ShowMenuRemoveTask(); break; case 3: ShowMenuTaskList(); break; } } while (menuselected != 4);
Si pienso que en esos casos seria mejor un switch porque estamos usando solo una comparacion igual y muchos valores a evaluar
Code smell es un término utilizado en el desarrollo de software para describir signos o indicios de que puede haber problemas en el código fuente. Estos indicios no son necesariamente errores de programación o bugs, pero sugieren que el código podría beneficiarse de mejoras, refactoring o una revisión más detallada.
Algunos otros son:
Recomiendo esta extension. SonarLint Detects and helps fix issues in your C# code locally in your IDE
Ejemplo con el Enum Menu y con switch:
do { menuSelected = ShowMainMenu(); switch ((Menu)menuSelected) { case Menu.Add: ShowMenuAdd(); break; case Menu.Remove: ShowMenuRemove(); break; case Menu.List: ShowMenuTaskList(); break; } } while ((Menu)menuSelected != Menu.Exit);
La traducción al español es "huele a chamusquina".
Esta clase me hizo recordar a cuando en mi trabajo pasado había un archivo con más de 20k lineas de código
Porque casteo implícitamente menuSelected a tipo Enum y no Menu.Add a Int.
Al momento qué convertimos menuSelected a Enum podemos comparar Enum con Enum. La opción que planteas también es válida al final es tratar de encontrar lo que mas convenga y que sea fácil de leer.
Code smell:
yo opte por swicht ya que se me hace mas practico para ste caso en especifico sin hacer el enum
do
{
menuSelected = ShowMainMenu();
switch (menuSelected)
{
case 1:
menuSelected = 1;
ShowMenuAdd();
break;
case 2:
menuSelected = 2;
ShowMenuRemove();
break;
case 3:
menuSelected = 3;
ShowMenuTaskList();
break;
case 4:
menuSelected = 4;
break;
default:
Console.WriteLine("porfavor ingresa una opcion valida");
break;
}
} while (menuSelected != 4);
Hay algún número máximo recomendable de parámetros ?
Normalmente 5 o 6, mas de eso se condisera demasiado pero cada escenario debe evaluarse
public enum Menu { Add =1, Remove = 2, List = 3, Exit = 4 }
"Tu código huele feo" puede ser literal.
Algo más en términos de español, un poco más vívida la misma idea, sería: "Tu código está escrito con las patas." o quizá: "Tu código no tiene pies ni cabeza."
Los "code smells" (o "malos olores del código") en C# se refieren a ciertos patrones o estructuras en el código que indican posibles problemas de diseño o implementación. Estos "smells" a menudo señalan áreas donde el código podría mejorarse para tener una mejor legibilidad, mantenibilidad y eficiencia. Aquí tienes algunos "code smells" comunes en C#:
1. **Métodos largos**: Los métodos excesivamente largos pueden ser difíciles de entender, probar y mantener. Divídelos en métodos más pequeños y enfocados.
2. **Clases grandes**: Similar a los métodos largos, las clases que son demasiado grandes a menudo indican que están haciendo demasiado. Considera dividirlas en clases más pequeñas y cohesivas.
3. **Obsesión por lo primitivo**: El uso excesivo de tipos primitivos (como cadenas o enteros) en lugar de crear tipos específicos del dominio puede conducir a un código menos expresivo y menos mantenible. Crea tipos personalizados para representar conceptos del dominio.
4. **Código repetido**: El código duplicado debe evitarse ya que conlleva sobrecarga de mantenimiento y posibles errores. Refactoriza el código duplicado en métodos o clases reutilizables.
5. **Listas de parámetros largas**: Los métodos con un gran número de parámetros pueden ser difíciles de usar y entender. Considera usar objetos de parámetros o dividir el método en otros más pequeños y enfocados.
6. **Intimidad inapropiada**: Las clases que están demasiado acopladas violan el principio de encapsulación y pueden ser difíciles de mantener y probar. Reduce las dependencias entre clases usando interfaces, inyección de dependencias u otros patrones de diseño.
7. **Nomenclatura inconsistente**: Las convenciones de nomenclatura inconsistentes hacen que el código sea más difícil de entender. Usa una nomenclatura clara y consistente en todo tu código.
8. **Envidia de características**: Cuando un método accede a los datos de otra clase más que a los suyos propios, puede indicar que el método pertenece a la otra clase. Considera mover el método a la clase que posee los datos.
9. **Grandes instrucciones SWITCH o IF-ELSE**: Las instrucciones switch y las cadenas if-else largas pueden ser difíciles de mantener y extender. Considera usar polimorfismo, el patrón de estrategia o tablas de búsqueda para manejar estos casos de manera más elegante.
10. **Grupos de datos**: Cuando grupos de campos de datos se pasan frecuentemente juntos, puede indicar que deberían estar juntos en su propia clase. Crea una nueva clase para encapsular campos de datos relacionados.
11. **Clases Dios**: Clases que saben o hacen demasiado, violando el Principio de Responsabilidad Única. Refactoriza estas clases en otras más pequeñas y enfocadas.
12. **Complejidad ciclomática**: Alta complejidad ciclomática en los métodos puede indicar una lógica demasiado compleja que es difícil de entender y probar. Refactoriza estos métodos en piezas más pequeñas y manejables.
Al identificar y abordar estos "code smells", puedes mejorar la calidad, legibilidad y mantenibilidad de tu base de código en C#.
Code Smells en C#:
¿Qué son?
Los Code Smells son indicadores de posibles problemas en tu código que pueden afectar su mantenimiento, legibilidad y probabilidad de errores.
Ejemplos comunes en C#:
¿Por qué son importantes?
Detectar y corregir los Code Smells a tiempo puede ayudarte a:
¿Cómo prevenirlos?
Hay unos libros recomendados para aprender estas buenas prácticas "The clean code" y de "Clean Coder" escritos por Bob Martin, conocido también Como Uncle Bob, y tienen acumulados muchos años de desarrollo y buenos prácticas
Vivi toda mi vida enganado pensando que utilizar los numero magicos estaba bien
De la clase anterior (Y)
using System; using System.Collections.Generic; namespace ToDo { internal class Program { private static List<string> taskList; //public static List<string> TL { get; set; } --> Malas practicas public static List<string> GetTaskList() { return taskList; } //public static List<string> TL { get; set; } --> Malas practicas public static void SetTaskList(List<string> value) { taskList = value; } static void Main(string[] args) { SetTaskList(new List<string>()); int menuSelected = 0; do { menuSelected = ShowMainMenu(); if (menuSelected == 1) { ShowMenuAdd(); } else if (menuSelected == 2) { ShowMenuRemove(); //Mala practica ShowMenuDos: debe decir siempre la función que cumple! } else if (menuSelected == 3) { ShowMenuTaskList(); //Mala practica ShowMenuTres: debe decir siempre la función que cumple! } } while (menuSelected != 4); } /// <summary> /// Show the main menu /// </summary> /// <returns>Returns option indicated by user</returns> public static int ShowMainMenu() { Console.WriteLine("----------------------------------------"); Console.WriteLine("Ingrese la opción a realizar: "); Console.WriteLine("1. Nueva tarea"); Console.WriteLine("2. Remover tarea"); Console.WriteLine("3. Tareas pendientes"); Console.WriteLine("4. Salir"); // Read line string taskIndex = Console.ReadLine(); return Convert.ToInt32(taskIndex); } public static void ShowMenuRemove() { try { Console.WriteLine("Ingrese el número de la tarea a remover: "); // Show current taks for (int i = 0; i < GetTaskList().Count; i++) { Console.WriteLine((i + 1) + ". " + GetTaskList()[i]); } Console.WriteLine("----------------------------------------"); string taskIndex = Console.ReadLine(); // Remove one position int indexToRemove = Convert.ToInt32(taskIndex) - 1; if (indexToRemove > -1) { if (GetTaskList().Count > 0) { string taskToRemove = GetTaskList()[indexToRemove]; GetTaskList().RemoveAt(indexToRemove); Console.WriteLine("Tarea " + taskToRemove + " eliminada"); } } } catch (Exception) { } } public static void ShowMenuAdd() { try { Console.WriteLine("Ingrese el nombre de la tarea: "); string task = Console.ReadLine(); GetTaskList().Add(task); Console.WriteLine("Tarea registrada"); } catch (Exception) { } } public static void ShowMenuTaskList() { if (GetTaskList() == null || GetTaskList().Count == 0) { Console.WriteLine("No hay tareas por realizar"); } else { Console.WriteLine("----------------------------------------"); for (int i = 0; i < GetTaskList().Count; i++) { Console.WriteLine((i + 1) + ". " + GetTaskList()[i]); } Console.WriteLine("----------------------------------------"); } } } }