👽
3ntr0 Apuntes
  • 📚Principios SOLID y Clean Code
    • 💲Deuda Técnica
    • 📌Mejorar Nombres
      • 📄Nombre según Tipo
      • 🚗Nombre Clases
      • ⚙️Nombres Funciones
    • ☑️Mejorar Funciones
    • ➿Principio DRY
    • 🧼Clean Code en Clases (POO)
      • 📘 Principio de Responsabilidad Única (SRP)
    • 🤮🇸 🇹 🇺 🇵 🇮 🇩 - Code Smells
      • 🚫Singleton
      • 🙏Tight Coupling
      • 🥸U P I D
      • 🚩Más Code Smells
  • 💕Angular
    • 🏍️Standalone
    • 🆚ngClass vs [class.clase]
    • 🩻Directivas
    • 🚩ElementRef
    • 🎀Decoradores
      • @HostListener
    • 🔄OnChanges
    • 🧪Testing - Jasmine y Karma
      • 🚀Módulo 1: Introducción a las pruebas unitarias
      • 🧫Módulo 2: Introducción a Jasmine
      • ⚒️Módulo 3: Introducción a Karma
      • 🔬Módulo 4: Pruebas unitarias en Angular
        • Código comentado Paso a Paso
      • 🕵️‍♂️Módulo 5: Técnicas avanzadas en Jasmine
      • 📚Módulo 6: Técnicas avanzadas en Karma
  • 🖼️HTML y CSS
    • 🖌️Custom Properties
    • Trucos
    • Brakepoints
  • 🧋Javascript
    • 🛠️Funciones
    • 📦Arrays
      • Every y Some
      • Map
      • Reduce
    • 😎Hoisting
    • 🪚Desestructuración
    • 🛻Programación Asincrona
      • 📞Callbacks
      • 🌟Promises
      • 🆚Promise.all Y Promise.any
      • 🚀Async/Await
    • Respuestas HTTP
    • 🧐Dudas Básicas
      • NodeList
      • 🧐Contextos de .this
      • 💭Parametro Rest
      • 🗨️arguments
      • 🙀JavaScript no tiene clases
      • 🆚Null vs Undefined
      • 🔎Operador in
      • 🟨Operador Spread
      • ❓Encadenamiento Opcional
      • 🔲Notación de Corchetes
      • ⛓️Coalescencia Nula (??)
      • 🆚Shallow Copy vs Deep Copy
      • 🆚.json VS JSON.parse
      • ⚙️Fetch wrapper
      • Sets
      • Maps
  • 📒Terminología
    • 💎Esenciales
    • Web
    • Javascript
  • 🌐GIT
    • Source Tree
  • 🧬React
    • 🫂Babel
  • 🔧 Fundamentos de C#
    • 🎛️General
      • 🧐Diferencia Equals y ==
      • 🧐Diferencia entre typeof y .GetType()
      • 📐Convenciones de Nomenclatura
    • 💠Summary
    • 📇.resx
    • 📄Strings
      • ⛓️Comparación de cadenas
    • 🧲Regex
    • 📦POO
      • 🚙Clases
        • ✏️Clase String
        • 📥Métodos de Acceso
        • ⚗️Métodos de Extensión
    • 🖇️Pattern Matching
    • 🚩Excepciones
    • Programación Asíncrona
    • 🔎LINQ
      • 🅿️PLINQ
  • 🌐 Desarrollo Web con ASP.NET
    • 🧬Modelos
      • 🗒️Data Annotations
        • 📑Lista
        • 🧪Atributos
          • 🛠️Atributos Personalizados
          • 🧰AttributeUsage
          • 📥Acceso a los atributos
        • 📚Documentación
    • 👷Servicios
      • ⭐Servicios Singleton
    • ⏳Sesiones
      • 🧭Temp Data
    • DbContext
      • 🔄Eager Loading
    • 🥽Manejo de Datos
      • 🗃️Archivos
        • 📤Subida de Archivos
        • ✏️Leer y Escribir
        • 🕓Manejo de Archivos Temporales
        • 🛡️Validación de Archivos en ASP.NET Core
      • Colecciones
        • 🪜Pila (Stack)
        • 🏇Cola (Queue)
      • 🩻Manejo de XMLs
        • 📂XmlDocument
          • 🎯XPath
        • 🧿XmlReader
    • ❤️Tips y Utilities
      • 🟰StringComparison
    • 🧰Debug Tools
      • 🧭Stopwatch
  • 🚀Razor
    • 🧱Configuración de Proyecto
    • 📃Pages
      • 🔸Método OnGet
      • 🔸Carpeta Models
      • 🔸Partial Pages
    • 🎨Layouts
      • Aplicar CSS A UNA PAGE
    • 🚴‍♂️Routing
    • 🏢_ViewImports
    • ✒️Sintaxis Razor
      • 😀Introducción
      • 📔Expresiones Implícitas
      • 📕Expresiones Explícitas
      • ✍️Renderizar Texto
      • 🧑‍🔬Class Page Model
      • 🔖Tag Helpers
        • 🔹asp-page
        • 🔹asp-append-version
        • 🔹asp-for
        • 🔹asp-items
        • 🔹asp-action y asp-controller
  • 🔮LUA
    • 🎯Fundamentos
    • ⌨️Entrada por Consola
    • 🔗Estructuras de Control
    • ⚒️Funciones
    • 📦Tablas
    • 📚Funciones y Librerías Estándar
    • 🦖POO
Con tecnología de GitBook
En esta página
  • 🔄 Composición frente a Herencia
  • 🧱 Ejemplo Práctico: Refactorización hacia Composición
  • ❗Algunas razones para ser cauteloso con la herencia y preferir composición en muchos casos:
  • Cuándo Usar Herencia
  • Cuándo Usar Composición
  1. Principios SOLID y Clean Code
  2. Clean Code en Clases (POO)

📘 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:

    1. Separar responsabilidades: Define clases independientes con sus propias responsabilidades únicas (Person, User, UserSettings).

    2. Utilizar Composición: Crear una nueva clase ConfiguracionUsuario que contenga instancias de Persona, Usuario y Configuración como propiedades.

    3. Mejorar la legibilidad: Usar interfaces para definir propiedades esperadas en los constructores, facilitando la inicialización de clases compuestas.

🔹Antes:

// Persona.ts
class Person {
    constructor(public name: string, public gender: string, public birthdate: Date) {}
}

// User.ts - Extiende de Persona
class User extends Person {
    constructor(name: string, gender: string, birthdate: Date, public email: string, public role: string) {
        super(name, gender, birthdate);
    }
}

// UserSettings.ts - Extiende de Usuario, agregando configuraciones específicas de usuario
class UserSettings extends User {
    constructor(name: string, gender: string, birthdate: Date, email: string, role: string, public workingDirectory: string, public lastOpenFolder: string) {
        super(name, gender, birthdate, email, role);
    }
}


const userSettings = new UserSettings(
    "Juan Pérez",      // name from Person
    "M",               // gender from Person
    new Date('1990-01-01'), // birthdate from Person
    "juan@example.com",// email from User
    "admin",           // role from User
    "/users/juan",     // workingDirectory from UserSettings
    "/users/juan/docs" // lastOpenFolder from UserSettings
);

🔹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.

// Person.ts
interface IPerson {
    name: string;
    gender: string;
    birthdate: Date;
}

// User.ts
interface IUser {
    email: string;
    role: string;
}

// Settings.ts
interface ISettings {
    workingDirectory: string;
    lastOpenFolder: string;
}

// UserProfile.ts
interface IUserProfile {
    person: IPerson;
    user: IUser;
    settings: ISettings;
}

Clase UserProfile con Composición

Luego 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.

class UserProfile {
    person: Person;
    user: User;
    settings: Settings;

    constructor({ person, user, settings }: IUserProfile) {
        this.person = new Person(person.name, person.gender, person.birthdate);
        this.user = new User(user.email, user.role);
        this.settings = new Settings(settings.workingDirectory, settings.lastOpenFolder);
    }
}


const userProfile = new UserProfile({
    person: {
        name: "Juan Pérez",
        gender: "M",
        birthdate: new Date('1990-01-01')
    },
    user: {
        email: "juan@example.com",
        role: "admin"
    },
    settings: {
        workingDirectory: "/users/juan",
        lastOpenFolder: "/users/juan/docs"
    }
});

Podemos aplicar este principio en todos los lenguajes orientados a objetos, por ejemplo C#

🔹Antes:

public class Person {
    public string Name { get; set; }
    public string Gender { get; set; }
    public DateTime Birthdate { get; set; }

    public Person(string name, string gender, DateTime birthdate) {
        Name = name;
        Gender = gender;
        Birthdate = birthdate;
    }
}

public class User : Person {
    public string Email { get; set; }
    public string Role { get; set; }

    public User(string name, string gender, DateTime birthdate, string email, string role)
        : base(name, gender, birthdate) {
        Email = email;
        Role = role;
    }
}


public class UserSettings : User {
    public string WorkingDirectory { get; set; }
    public string LastOpenFolder { get; set; }

    public UserSettings(string name, string gender, DateTime birthdate, string email, string role, string workingDirectory, string lastOpenFolder)
        : base(name, gender, birthdate, email, role) {
        WorkingDirectory = workingDirectory;
        LastOpenFolder = lastOpenFolder;
    }
}

🔹Después:

public class Person {
    public string Name { get; set; }
    public string Gender { get; set; }
    public DateTime Birthdate { get; set; }

    public Person(string name, string gender, DateTime birthdate) {
        Name = name;
        Gender = gender;
        Birthdate = birthdate;
    }
}

public class User {
    public string Email { get; set; }
    public string Role { get; set; }

    public User(string email, string role) {
        Email = email;
        Role = role;
    }
}

public class Settings {
    public string WorkingDirectory { get; set; }
    public string LastOpenFolder { get; set; }

    public Settings(string workingDirectory, string lastOpenFolder) {
        WorkingDirectory = workingDirectory;
        LastOpenFolder = lastOpenFolder;
    }
}

public class UserProfile {
    public Person Person { get; set; }
    public User User { get; set; }
    public Settings Settings { get; set; }

    public UserProfile(Person person, User user, Settings settings) {
        Person = person;
        User = user;
        Settings = settings;
    }
}

❗Algunas razones para ser cauteloso con la herencia y preferir composición en muchos casos:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

AnteriorClean Code en Clases (POO)Siguiente🇸 🇹 🇺 🇵 🇮 🇩 - Code Smells

Última actualización hace 1 año

📚
🧼