El Diseño Simple es uno de los valores y prácticas centrales de Extreme Programming (XP). En un entorno donde los requisitos cambian constantemente, crear diseños complejos para futuros inciertos es contraproducente. XP aboga por encontrar la solución más simple que funcione para los requisitos actuales, confiando en la refactorización para evolucionar el diseño a medida que surgen nuevas necesidades.
Este principio es conocido popularmente por el acrónimo YAGNI: "You Ain't Gonna Need It" (No vas a necesitarlo). La idea es resistir la tentación de añadir funcionalidades o complejidad al código basándose en especulaciones sobre el futuro.
En lugar de preguntarse "¿Qué podría necesitar este sistema en el futuro?", el equipo de XP se pregunta "¿Cuál es la forma más simple de resolver el problema que tenemos hoy?". Este enfoque tiene varias ventajas:
La sobreingeniería es el acto de diseñar y construir un sistema de una manera mucho más compleja de lo necesario para los requisitos actuales. Es el enemigo directo del Diseño Simple. A menudo, los desarrolladores caen en la sobreingeniería por buenas intenciones: quieren crear un sistema robusto, flexible y preparado para cualquier eventualidad futura.
Sin embargo, esto suele llevar a:
XP confía en que es mejor y más barato adaptar un diseño simple cuando sea necesario, gracias a prácticas como TDD y la refactorización, que empezar con un diseño complejo basado en la especulación.
El principio KISS es una máxima de diseño que se originó en la Marina de los EE. UU. en 1960 y que ha sido adoptada masivamente en el mundo del desarrollo de software. Sostiene que la mayoría de los sistemas funcionan mejor si se mantienen simples en lugar de complejos. Por lo tanto, la simplicidad debe ser un objetivo clave en el diseño, y se debe evitar la complejidad innecesaria.
En XP, KISS se manifiesta en varias prácticas:
Un equipo de XP evalúa un diseño preguntándose: "¿Podemos hacerlo más simple?". Si la respuesta es sí, y la solución más simple sigue cumpliendo todos los requisitos, esa es la dirección a seguir.
Imaginemos que necesitamos una función que devuelva un saludo en diferentes idiomas. Un desarrollador podría pensar en el futuro y decidir aplicar un patrón de diseño complejo como el Patrón Factory.
Este enfoque es flexible, pero excesivamente complejo para dos idiomas.
# Interfaz del saludador
class Saludo:
def saludar(self):
raise NotImplementedError
# Implementaciones concretas
class SaludoEspanol(Saludo):
def saludar(self):
return "Hola"
class SaludoIngles(Saludo):
def saludar(self):
return "Hello"
# Factory para crear el objeto correcto
class SaludoFactory:
def obtener_saludo(self, idioma):
if idioma == 'es':
return SaludoEspanol()
elif idioma == 'en':
return SaludoIngles()
# Uso
factory = SaludoFactory()
saludo_es = factory.obtener_saludo('es')
print(saludo_es.saludar()) # Imprime "Hola"
Aplicando los principios de Diseño Simple, creamos solo lo necesario para los requisitos actuales. Un diccionario es suficiente y mucho más fácil de leer y mantener.
SALUDOS = {
'es': 'Hola',
'en': 'Hello',
}
def obtener_saludo(idioma):
"""Devuelve el saludo para un idioma, con un valor por defecto."""
return SALUDOS.get(idioma, 'Idioma no soportado')
# Uso
print(obtener_saludo('es')) # Imprime "Hola"
print(obtener_saludo('fr')) # Imprime "Idioma no soportado"
Esta segunda solución es drásticamente más simple. Tiene menos líneas de código, ninguna clase o indirección innecesaria, y es trivial de entender. Si en el futuro se necesita una lógica más compleja (por ejemplo, saludos que dependan de la hora del día), el equipo puede refactorizar esta solución simple con la confianza que le dan sus pruebas. Empezar con la solución compleja desde el principio habría sido un desperdicio de esfuerzo.