Microoptimizaciones Final - nicolasjaramillocely99/Proyecto_Ingenieria_Software_Aplicaciones_Moviles GitHub Wiki
Eliminar "Code Smells" detectados por SonarQube relacionados con el uso no idiomático de Kotlin y definiciones de interfaces, alineando el código con las mejores prácticas del lenguaje sin alterar la lógica de negocio.
-
"Replace function call with indexed accessor" (kotlin:S6518)
-
Ubicación:
AddTrackViewModel.kt,AlbumDetailViewModel.kt,VinylsApp.kt -
Causa: Uso explícito de la función
.get()cuando Kotlin ofrece sobrecarga de operadores para usar corchetes[].
-
Ubicación:
-
"Make this interface functional or replace it with a function type" (kotlin:S6516)
-
Ubicación:
CollectorApiService.kt -
Causa: Una interfaz con un solo método abstracto (SAM) no estaba marcada como
fun interface.
-
Ubicación:
Principio Aplicado: "Kotlin Idioms & Syntactic Sugar"
Optimizar la sintaxis para hacerla más concisa y aprovechar las características del compilador de Kotlin (SAM Conversions).
// AddTrackViewModel.kt & AlbumDetailViewModel.kt
private val albumId: Int = savedStateHandle.get<Int>("albumId") ?: 0
// VinylsApp.kt
if (backStackEntry.savedStateHandle.get<Boolean>("album_created") == true) { ... }
// AddTrackViewModel.kt & AlbumDetailViewModel.kt
private val albumId: Int = savedStateHandle["albumId"] ?: 0
// VinylsApp.kt
if (backStackEntry.savedStateHandle["album_created"] == true) { ... }
- Se elimina la necesidad de especificar el tipo genérico explícitamente
<Int>o<Boolean>en la llamada, ya que el compilador lo infiere o el operador lo maneja mejor. - Lectura más natural (estilo array/mapa).
// CollectorApiService.kt
interface CollectorApiService {
@GET("collectors")
suspend fun getCollectors(): Response<List<CollectorDto>>
}
// CollectorApiService.kt
fun interface CollectorApiService {
@GET("collectors")
suspend fun getCollectors(): Response<List<CollectorDto>>
}
- Al agregar
fun, habilitamos la Conversión SAM. Esto permite que, en tests o implementaciones futuras, se pueda instanciar la interfaz usando una lambda simple en lugar de crear un objeto anónimo completo.
| Métrica | Antes | Después | Mejora |
|---|---|---|---|
| Issues SonarQube | ~5 Code Smells | 0 | ✅ 100% resuelto |
| Verbosity | Alta (.get) | Baja ([]) | ✅ Código más limpio |
| SAM Support | No | Sí | ➕ Flexibilidad para Testing |
| Legibilidad | Media | Alta | ✅ Mejorada |
| Funcionalidad | ✅ | ✅ | ✅ Sin cambios |
El uso de corchetes [] para acceder a mapas o contenedores tipo SavedStateHandle es el estándar en Kotlin. Hace que el código se sienta nativo y menos parecido a Java.
Se eliminaron llamadas a funciones explícitas y tipos genéricos redundantes, permitiendo leer la lógica de asignación más rápido.
Al convertir CollectorApiService en una fun interface, facilitamos la creación de Mocks o Stubs simples en pruebas unitarias usando lambdas:
-
Antes:
object : CollectorApiService { override suspend fun... } -
Ahora posible:
CollectorApiService { ... }
Se eliminan advertencias de SonarQube, mejorando la calificación técnica del proyecto ("Technical Debt Ratio").
- Clean Code: "El código debe ser fácil de leer por humanos". La sintaxis de acceso indexado es universalmente entendida como "obtener valor por clave".
- Idiomatic Programming: Escribir código aprovechando las características específicas del lenguaje, no traduciendo literalmente de otros lenguajes.
Esta serie de microoptimizaciones, aunque pequeñas en líneas de código, elevan la calidad profesional del proyecto al adherirse estrictamente a las guías de estilo de Kotlin y eliminar deuda técnica reportada por herramientas de análisis estático.
Tipo de refactorización: Syntax Cleanup & Semantic Modernization
Impacto: Mejora en mantenibilidad y legibilidad; cero riesgo funcional.
Refactorizar la pantalla AddTrackScreen para reducir su Complejidad Cognitiva (de 18 a <15), dividiendo un Composable monolítico en componentes pequeños, reutilizables y fáciles de mantener, alineándose con los principios de "Declarative UI" de Jetpack Compose.
- "Refactor this method to reduce its Cognitive Complexity from 18 to the 15 allowed" (kotlin:S3776)
-
Ubicación:
AddTrackScreen.kt
- 🔴 Monolito UI: Un solo método
AddTrackScreencontenía toda la lógica de navegación, manejo de estados, estructuraScaffold, y la definición detallada de 4 campos de texto más botones. - 🔴 Código Repetido: La configuración de estilos y colores (
OutlinedTextFieldDefaults.colors(...)) se repetía idénticamente 4 veces. - 🔴 Anidación Profunda: Múltiples niveles de anidación (Scaffold → Column → Cards → OutlinedTextFields → Listeners).
Principio Aplicado: "Extract Composable & Composition over Inheritance"
Dividir la UI en componentes funcionales pequeños y específicos.
-
Orquestador (
AddTrackScreen): Solo maneja elViewModel,Statey eventos de navegación. -
Contenedor (
AddTrackContent): Define la estructura visual principal (Column, Scroll). -
Componentes Atómicos: Composables individuales para cada campo (
TrackNameField,TrackDurationField, etc.) y secciones (AddTrackTopBar,ActionButtons).
@Composable
fun AddTrackScreen(...) {
// Lógica de estado mezclada con UI
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
Scaffold(...) {
Column(...) {
// Campo 1
OutlinedTextField(
value = uiState.name,
colors = OutlinedTextFieldDefaults.colors(...), // 10 líneas de config
...
)
// Campo 2 (Repite config)
OutlinedTextField(
value = uiState.duration,
colors = OutlinedTextFieldDefaults.colors(...), // 10 líneas de config
...
)
// ... Campo 3, Campo 4, Botones ...
}
}
}
@Composable
fun AddTrackScreen(...) {
// Solo orquestación
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
Scaffold(
topBar = { AddTrackTopBar(...) }
) { padding ->
AddTrackContent(uiState, ...)
}
}
@Composable
private fun AddTrackContent(...) {
Column(...) {
ErrorMessage(uiState.error)
TrackNameField(uiState.name, ...)
TrackDurationField(uiState.duration, ...)
// ...
ActionButtons(...)
}
}
// Componentes reutilizables y limpios
@Composable
private fun TrackNameField(...) {
OutlinedTextField(
...,
colors = getTextFieldColors() // Configuración centralizada
)
}
| Métrica | Antes | Después | Mejora |
|---|---|---|---|
| Complejidad Cognitiva | 18 (Crítico) | ~3 (Bajo) | ✅ Reducción drástica |
| Líneas (Screen principal) | ~240 | ~30 | ✅ Más legible |
| Duplicación de Código | Alta (Estilos) | Nula | ✅ DRY aplicado |
| Mantenibilidad | Difícil | Alta | ✅ Componentes aislados |
Al leer AddTrackContent, ves qué componentes hay (TrackNameField, ActionButtons), no cómo están implementados detalle por detalle.
Si necesitas cambiar el color de todos los inputs, solo modificas la función getTextFieldColors(). Antes tenías que editar 4 lugares distintos.
-
AddTrackScreen: ¿Qué datos muestro? (Estado) -
AddTrackContent: ¿Cómo organizo los datos? (Layout) -
TrackNameField: ¿Cómo se ve este dato específico? (Widget)
Se eliminó la advertencia de complejidad, haciendo el código robusto y profesional.
- Single Responsibility Principle (SRP): Cada Composable hace una sola cosa (mostrar un campo, mostrar un botón, organizar el layout).
-
Don't Repeat Yourself (DRY): Extracción de estilos comunes en
getTextFieldColors. - Declarative UI: El código describe la UI en términos de "qué es", delegando los detalles de implementación a sub-componentes.
La refactorización transformó una pantalla compleja y repetitiva en un conjunto de componentes limpios y modulares. Esto no solo satisface las métricas de calidad de código, sino que facilita enormemente futuras expansiones o modificaciones de la interfaz de usuario.
Tipo de refactorización: UI Component Extraction
Impacto: Alto en mantenibilidad y calidad de código; nulo en riesgo funcional.