Qué es SOLID y por qué importa en Unity#
SOLID son cinco principios para escribir código que crece sin pudrirse. En Unity es fácil acabar con un PlayerController de 800 líneas que hace movimiento, vida, inventario, sonido y UI: tocar una cosa rompe otra. SOLID es el antídoto. No son reglas sagradas, son guías con criterio.
S — Responsabilidad única#
Cada clase, una razón para cambiar. Divide ese Player gigante en piezas:
// ❌ Una clase que lo hace todo
class Player : MonoBehaviour { /* mover, vida, inventario, sonido, UI... */ }
// ✅ Componentes con una responsabilidad cada uno
class PlayerMovement : MonoBehaviour { }
class Health : MonoBehaviour { }
class Inventory : MonoBehaviour { }Unity ya te empuja a esto: su modelo es composición de componentes pequeños. Aprovéchalo.
O — Abierto/cerrado#
Abierto a extensión, cerrado a modificación: añadir un caso nuevo no debería obligarte a editar código que ya funciona. En vez de un switch que crece sin fin, usa polimorfismo:
// ✅ Añadir un arma nueva = una clase nueva, sin tocar las demás
abstract class Arma : MonoBehaviour { public abstract void Disparar(); }
class Pistola : Arma { public override void Disparar() { /* ... */ } }
class Escopeta : Arma { public override void Disparar() { /* ... */ } }Los ScriptableObject son perfectos para esto: cada variante es un asset, sin tocar código.
L — Sustitución de Liskov#
Una subclase debe poder usarse donde se espera la base sin sorpresas. Si Pinguino hereda de Ave pero rompe Volar(), la jerarquía está mal: quizá Volar no debería estar en Ave. Regla práctica: si al heredar tienes que lanzar "no soportado" o dejar métodos vacíos, replantea la herencia (o usa interfaces).
I — Segregación de interfaces#
Mejor varias interfaces pequeñas que una enorme que obliga a implementar métodos que no usas:
// ✅ Interfaces pequeñas y componibles
interface IDanable { void RecibirDano(int cantidad); }
interface ICurable { void Curar(int cantidad); }
// Un cofre es dañable pero no curable; un jugador, ambas
class Cofre : MonoBehaviour, IDanable { public void RecibirDano(int c) { } }Encaja de maravilla con Unity: puedes hacer GetComponent<IDanable>() y tratar a todo lo dañable por igual sin que compartan clase base.
D — Inversión de dependencias#
Depende de abstracciones, no de clases concretas. Un Enemigo no debería crear su propio AudioManager concreto: que reciba un IAudio. Es la base de la inyección de dependencias, que tiene su propio artículo.
El mayor error con SOLID es sobreingeniar: interfaces y capas para un juego minúsculo que nunca las necesitará. Aplica SOLID cuando el dolor aparece (una clase que no para de crecer, cambios que rompen cosas lejanas), no de forma preventiva en un prototipo. Código simple > código "perfecto" que nadie entiende.
Errores frecuentes#
- El God Object: un
GameManager/Playerque lo sabe y lo hace todo. - Herencia profunda donde una interfaz o composición sería más clara (rompe Liskov).
- Aplicar las cinco letras a rajatabla en un prototipo (sobreingeniería).
- Confundir "una clase por archivo" con "responsabilidad única" (no es lo mismo).
Ponte a prueba#
Resumen#
SOLID mantiene el código sano al crecer: S una responsabilidad por clase (encaja con los componentes de Unity), O extiende sin modificar (polimorfismo, ScriptableObjects), L subclases sin sorpresas, I interfaces pequeñas y componibles (GetComponent<IDanable>()), D depende de abstracciones (base de la inyección de dependencias). Son guías con criterio: aplícalas cuando el proyecto lo pida, sin sobreingeniar un prototipo.