📘 Principio de Responsabilidad Única (SRP)
Estos apuntes estan basados en Typescript
Definición: Cada clase o módulo debe tener una sola razón para cambiar. Esto significa que una clase debe realizar solo una tarea o tener una responsabilidad.
Aplicación: En el contexto de TypeScript o cualquier otro lenguaje orientado a objetos, el SRP nos guía para modularizar el código de manera que cada clase maneje únicamente los aspectos de la aplicación estrechamente relacionados con su propósito.
🔄 Composición frente a Herencia
Definición:
Herencia: Extiende una clase base para crear una clase derivada que hereda propiedades y comportamientos, pero puede llevar a jerarquías complejas y código rígido.
Composición: Construye objetos más complejos combinando objetos simples y delegando tareas a ellos, lo que promueve una mayor flexibilidad y reutilización del código.
Preferencia: Se recomienda priorizar la composición sobre la herencia para evitar complejidades y dependencias innecesarias entre clases.
🧱 Ejemplo Práctico: Refactorización hacia Composición
Problema Identificado: Clases extendidas múltiples veces (Person ➡️ User ➡️ UserSettings), lo que complica el mantenimiento y la comprensión del código.
Solución:
Separar responsabilidades: Define clases independientes con sus propias responsabilidades únicas (Person, User, UserSettings).
Utilizar Composición: Crear una nueva clase
ConfiguracionUsuario
que contenga instancias dePersona
,Usuario
yConfiguración
como propiedades.Mejorar la legibilidad: Usar interfaces para definir propiedades esperadas en los constructores, facilitando la inicialización de clases compuestas.
🔹Antes:
🔹Después:
Definición de Interfaces
Vamos a definir interfaces para Person
, User
, y Settings
que especifiquen las propiedades esperadas para cada una. Además, agregaremos una para UserProfile
que compone las tres anteriores.
Clase UserProfile
con Composición
UserProfile
con ComposiciónLuego ajustamos la clase UserProfile
para que acepte un objeto que cumpla con la interfaz IUserProfile
, lo que nos permite inicializar UserProfile
con composición de una manera más estructurada y segura.
Podemos aplicar este principio en todos los lenguajes orientados a objetos, por ejemplo C#
🔹Antes:
🔹Después:
❗Algunas razones para ser cauteloso con la herencia y preferir composición en muchos casos:
Acoplamiento Fuerte: La herencia crea una dependencia directa entre la clase base y la subclase. Esto significa que cambiar la clase base puede tener efectos no deseados en las subclases, lo que puede ser problemático en sistemas grandes y complejos.
Jerarquías Complejas: Una profunda jerarquía de herencia puede hacer que el código sea difícil de entender y mantener. Puede ser complicado seguir el flujo de la lógica a través de múltiples niveles de clases.
Flexibilidad: La composición ofrece mayor flexibilidad en cómo se pueden combinar y reutilizar los objetos. Permite cambiar el comportamiento de los objetos en tiempo de ejecución agregando, cambiando o quitando componentes, lo cual no es posible con la herencia fija.
Reutilización de Código: Aunque la herencia permite la reutilización de código, este beneficio también puede lograrse mediante la composición, a menudo de manera más clara y flexible.
Polimorfismo: Aunque la herencia se usa para lograr polimorfismo, interfaces y clases abstractas en lenguajes como C# y Java también permiten polimorfismo sin forzar una relación de herencia directa.
Cuándo Usar Herencia
Usa herencia cuando necesites modelar una relación clara de "es un(a)" entre objetos, donde subclases específicas comparten y extienden comportamientos y atributos de una clase base común. La herencia es ideal para:
Aplicar polimorfismo, permitiendo que objetos de diferentes clases sean tratados como objetos de una clase base.
Centralizar código común, facilitando la mantenibilidad y reduciendo la duplicación.
Extender funcionalidades de clases base de manera jerárquica y organizada.
Cuándo Usar Composición
Opta por composición para construir clases más flexibles y mantenibles, combinando objetos de otras clases sin crear una dependencia jerárquica rígida. La composición es preferible cuando:
No existe una relación clara de "es un(a)" o la relación es más bien de "tiene un(a)".
Quieres cambiar o combinar comportamientos en tiempo de ejecución.
Buscas reducir el acoplamiento entre clases, facilitando el testeo y la extensión del código sin alterar las clases existentes.
Necesitas reutilizar funcionalidades de múltiples fuentes.
Última actualización