Principios SOLID en Kotlin: teoría y práctica de la A a la Z

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Principios SOLID en Kotlin: de la teoría a la práctica

\n\n

SOLID es el acrónimo de cinco principios fundamentales de la programación orientada a objetos, formulados por Robert Martin. Estos principios ayudan a crear código flexible, mantenible y escalable. En este artículo, analizaremos en detalle cada principio SOLID con ejemplos del desarrollo real en Kotlin.

\n\n

Comprender SOLID es críticamente importante para cualquier desarrollador de Kotlin, especialmente al trabajar con frameworks como Spring Boot o Ktor. Estos principios son la base de la arquitectura limpia y los patrones de diseño.

\n\n

1. Single Responsibility Principle (SRP) — Principio de responsabilidad única

\n\n

Esencia: Cada clase debe tener una sola razón para cambiar. En otras palabras, la clase debe ser responsable de una única funcionalidad.

\n\n

En Kotlin, este principio es especialmente importante debido al soporte del estilo funcional. Veamos un mal ejemplo:

\n\n
// ❌ Violación de SRP: la clase se ocupa tanto del registro como de la BD\nclass UserService {\n    fun saveUser(user: User) {\n        // Registro\n        println("Saving user: ${user.name}")\n        // Lógica de negocio\n        validateUser(user)\n        // Trabajo con BD\n        database.insert(user)\n    }\n}\n
\n\n

Corrijamos la situación dividiendo la responsabilidad:

\n\n
// ✅ Enfoque correcto: cada responsabilidad en una clase separada\nclass Logger {\n    fun log(message: String) = println("[LOG] $message")\n}\n\nclass UserValidator {\n    fun validate(user: User): Boolean = user.name.isNotBlank()\n}\n\nclass UserRepository {\n    fun save(user: User) { /* guardar en BD */ }\n}\n\nclass UserService(\n    private val logger: Logger,\n    private val validator: UserValidator,\n    private val repository: UserRepository\n) {\n    fun saveUser(user: User) {\n        logger.log(\"Saving user: ${user.name}\")\n        if (validator.validate(user)) {\n            repository.save(user)\n        }\n    }\n}\n
\n\n

2. Open-Closed Principle (OCP) — Principio de abierto/cerrado

\n\n

Esencia: Las clases deben estar abiertas para la extensión, pero cerradas para la modificación. La nueva funcionalidad debe agregarse mediante herencia o composición, sin modificar el código existente.

\n\n

En Kotlin, las clases selladas (sealed) y las interfaces son ideales para implementar OCP:

\n\n
// ❌ Violación de OCP: al agregar un nuevo tipo de figura, hay que modificar la clase\nclass AreaCalculator {\n    fun calculate(shape: Any): Double = when (shape) {\n        is Circle -> 3.14 * shape.radius * shape.radius\n        is Rectangle -> shape.width * shape.height\n        else -> throw IllegalArgumentException()\n    }\n}\n
\n\n

Implementación correcta usando polimorfismo:

\n\n
// ✅ Enfoque correcto: las nuevas figuras se agregan sin modificar el código existente\ninterface Shape {\n    fun area(): Double\n}\n\nclass Circle(val radius: Double) : Shape {\n    override fun area() = Math.PI * radius * radius\n}\n\nclass Rectangle(val width: Double, val height: Double) : Shape {\n    override fun area() = width * height\n}\n\n// Ahora se puede agregar un triángulo sin modificar AreaCalculator\nclass Triangle(val base: Double, val height: Double) : Shape {\n    override fun area() = 0.5 * base * height\n}\n\nclass AreaCalculator {\n    fun calculate(shape: Shape) = shape.area()\n}\n
\n\n

3. Liskov Substitution Principle (LSP) — Principio de sustitución de Barbara Liskov

\n\n

Esencia: Los objetos de una clase base deben poder ser reemplazados por objetos de sus clases derivadas sin alterar el funcionamiento del programa. La subclase no debe debilitar las precondiciones ni fortalecer las postcondiciones.

\n\n

Veamos el ejemplo clásico con un rectángulo y un cuadrado:

\n\n
// ❌ Violación de LSP: el cuadrado no puede reemplazar al rectángulo\nopen class Rectangle(\n    open var width: Int,\n    open var height: Int\n) {\n    fun area() = width * height\n}\n\nclass Square(sid                

Blogs

Book Recommendations