Pruebas y calidad de código

[Código oloroso #4] Guía completa del God Object

Un God Object es una clase que crece al absorber responsabilidades y datos de su entorno. Es el archivo que más cambia y más conflictos genera en el equipo. Aquí se resumen las señales de crecimiento, el orden de extracción y dos formas de fallar al dividirlo.

7 min de lectura
Imagen de portada de [Código oloroso #4] Guía completa del God Object

Todo proyecto tiene uno de esos archivos. Lo abres y la barra de desplazamiento se vuelve fina como un hilo.

Este contenido continúa el artículo anterior Código oloroso #3.

Puede llamarse AppManager, MainViewController o DataStore. También es el archivo que más cambia y más conflictos provoca en el equipo.

Es un God Object.

En 『AntiPatterns』, publicado en 1998, se denomina The Blob (Información de publicación). Describe una masa que crece absorbiendo continuamente responsabilidades y datos del entorno.

Qué es un God Object

No se decide solo por el número de líneas. Un tipo de 2.000 líneas que hace una sola cosa sigue siendo simplemente un tipo grande (Estudio para identificar God Class).

Lo que distingue a un God Object es el alcance de lo que conoce. Si se cumplen las tres condiciones, casi seguro lo es.

  • **Sabe demasiado.**Red, base de datos, estado de pantalla, credenciales y pagos conviven en un solo tipo. importLa lista lo delata.
  • **Demasiadas cosas lo conocen.**Se referencia desde todo el proyecto. Si es un singleton, las rutas de referencia quedan ocultas y el problema empeora.
  • **El estado está disperso.**Tiene treinta propiedades, pero nadie sabe qué combinaciones son válidas.

Ya no se puede responder si es posible que isLoading sea true mientras error tampoco sea nil.

En iOS, este papel suele estar claro: el controlador de vista.

El diseño de una pantalla, las llamadas de red, la fuente de datos de la tabla, las transiciones y la gestión del estado terminan en un solo archivo.

Esta estructura se llama Massive View Controller. También son habituales los tipos cuyo nombre incluye AppDelegate y Manager.

Por qué sigue creciendo

Nadie quiso construirlo así. Ese es el punto.

Un God Object no es una decisión de diseño, sino el resultado de la gravedad.

Supongamos que hay que añadir una función. Todos los datos necesarios ya están en esta clase.

Elección Coste
Añadir un método a la clase existente 20 minutos
Crear un tipo nuevo Pasar dependencias, preparar la inicialización y gestionar el ciclo de vida: medio día

Cada elección es razonable, pero siempre apunta en la misma dirección. Así, un archivo de 200 líneas se convierte en uno de 4.000 tres años después.

Cada etapa de crecimiento tiene una justificación legítima, por eso es difícil deshacerla.

Además aparece el efecto de las ventanas rotas. Nadie protesta por añadir 50 líneas a un archivo que ya tiene 3.000.

La resistencia psicológica es distinta cuando se añaden 50 líneas a un archivo de 200.

Diagrama de dependencias de un controlador de vista de 4000 líneas que conoce red, DB, sesión y pagos
El número de flechas equivale a la dificultad de las pruebas

Dónde duele

El daño aparece de cuatro formas.

  • **Las pruebas se vuelven imposibles.**Para crear este tipo hacen falta red, base de datos y sesión de usuario. Quieres validar una lógica de cálculo pura, pero el servidor debe estar activo.
  • **Se bloquea el trabajo en paralelo.**Tres personas desarrollan funciones distintas, pero todas modifican el mismo archivo. Los conflictos son constantes y los cambios se mezclan durante la revisión.
  • **No se puede conocer el alcance de una modificación.**Para predecir qué romperá cambiar una propiedad hay que conocer las 4.000 líneas. Como nadie las conoce, se añade otra propiedad y el archivo vuelve a crecer.
  • **No se puede reutilizar.**Otra pantalla necesita su lógica de formato de fechas, pero extraerla arrastra toda la clase. Así que se copia y pega.

La primera es especialmente grave. Sin pruebas no se puede dividir; como no se divide, sigue creciendo.

Este ciclo es la verdadera razón por la que un God Object sobrevive tanto tiempo.

Cómo detectar que crece

Usa señales, no sensaciones.

Señal 1 · Frecuencia de cambios en Git

Es el indicador más práctico. Si extraes la lista de archivos más modificados durante el último año, el God Object suele aparecer arriba.

git log --format=format: --name-only --since=1.year.ago \
  | grep '\.swift$' | sort | uniq -c | sort -rn | head -20

Cambiar a menudo indica que el archivo cambia por razones distintas: una medición real de la violación del principio de responsabilidad única.

Señal 2 · Cohesión

Si los métodos solo tocan conjuntos distintos de propiedades, probablemente ese tipo sean en realidad dos.

Cinco métodos que usan solo A y B, y seis que usan solo C y D, ya muestran un posible límite.

Señal 3 · Regla de longitud del tipo

Con SwiftLint, type_body_length y file_length vienen activadas de forma predeterminada.

La advertencia predeterminada es de 250 líneas para el cuerpo del tipo y 400 para el archivo; puedes ajustarla al proyecto. Los números son arbitrarios, pero cruzar el límite inicia una conversación, y eso ya tiene valor.

Orden de extracción

Reescribirlo todo de una vez casi siempre falla. Hay un orden.

# Etapa Qué hacer
1 Crear una red Si no hay pruebas, empieza por crear pruebas de caracterización
2 Separar la lógica sin estado Extrae primero el formato de fechas, la validación de cadenas y el cálculo de importes
3 Separar grupos de estado Agrupa en un tipo las propiedades que cambian juntas
4 Separar por rol Divide en fuente de datos, coordinador, modelo de vista y controladores de vista hijos
5 Cortar la ruta de absorción Define dónde va el código nuevo y establece un umbral

Una prueba de caracterización no registra si el código es correcto, sino cómo funciona ahora. Es una técnica descrita por Michael Feathers en 『Working Effectively with Legacy Code』.

El objetivo es moverlo conservando el comportamiento, así que el comportamiento actual es la línea base.

La segunda etapa es la más fácil: extrae lógica que no usa el estado de la instancia. Aquí el archivo suele reducirse visiblemente.

En la tercera etapa, los enum de Swift resultan útiles. Si tres estados de pantalla siempre cambian juntos, son un único objeto de estado.

// Antes: pueden expresarse combinaciones no válidas
var isLoading = false
var items: [Item] = []
var error: Error?

// Después: solo existe uno de los tres
enum ViewState {
    case loading
    case loaded([Item])
    case failed(Error)
}

Agruparlos con enum elimina las combinaciones imposibles.

En iOS, la ruta de la cuarta etapa está clara: fuente de datos de tabla en un tipo separado, transiciones en un coordinador, estado y reglas de presentación en un modelo de vista, y pantallas grandes en controladores de vista hijos.

Si falta la quinta etapa, en seis meses volverá a ser igual. Define dónde va el código nuevo y establece un umbral con reglas de longitud o acuerdos de revisión para impedir que la siguiente función vuelva al mismo archivo.

Proceso de dividir gradualmente una clase monolítica en tipos pequeños
El objetivo no es crear fragmentos, sino que cada cambio tenga un solo motivo

Dos formas de fallar al dividirlo

Forma de fallo Qué ocurre
Cambiar solo los nombres Dividiste AppManager en UserManager, DataManager y NetworkManager, pero los tres se referencian por completo. El God Object solo se convirtió en un God Cluster.
Dividirlo demasiado Si divides una clase de 4.000 líneas en 40 tipos, esta vez desaparece el flujo. Es el código ravioli del artículo anterior.

Al dividir, comprueba también que las dependencias fluyan en una sola dirección.

El objetivo no es crear fragmentos, sino que cada fragmento cambie solo por su propio motivo. El número de fragmentos es solo el resultado.

Resumen

  • Un God Object se identifica por lo que conoce, no por su tamaño. Si conoce mucho, muchos lo conocen y el estado está disperso, encaja.
  • Crece no por ser un mal diseño, sino porque cada vez se elige la opción más barata.
  • El mayor daño es que no se puede probar. Sin pruebas no se divide; sin división sigue creciendo.
  • La lista superior de frecuencia de cambios en Git es un indicador práctico.
  • El orden es pruebas de caracterización → lógica sin estado → grupos de estado → separación por roles; al final se bloquea el camino para que vuelva a crecer.

El próximo artículo trata el problema posterior a dividir un God Object: tener que modificar doce archivos para cambiar una función, la cirugía de escopeta.

Fuentes y criterios de verificación

  • AntiPatterns — Wiley · datos oficiales · verificado 2026-08-17 · base: información de publicación de AntiPatterns de 1998 y contexto de The Blob
  • Human Perception on God Class Detection — Springer Nature · texto original del artículo · verificado 2026-08-17 · base: experimento controlado sobre diferencias entre personas y herramientas al identificar God Class

Seguir leyendo