¿Qué es la herencia en la programación orientada a objetos?
La herencia es uno de los conceptos clave en la programación orientada a objetos que permite a las clases compartir atributos y métodos. Cuando hablamos de herencia, nos referimos a la relación estrecha que existe entre una subclase y una superclase, donde la subclase hereda comportamientos y funcionalidades de la superclase para evitar la redundancia de código.
Aspectos importantes de la herencia
Acoplamiento estrecho: La subclase y la superclase están estrechamente vinculadas. Cambios en la superclase pueden afectar inmediatamente a las subclases derivadas.
Limitaciones en la interfaz: Una subclase no tiene la capacidad de reducir la interfaz pública de su superclase. Esto significa que no puede eliminar o modificar métodos existentes, aunque sí puede extender su funcionalidad añadiendo nuevos métodos.
Taxonomías complejas: La herencia puede llevar a la creación de jerarquías extensas y complejas. Este fenómeno, conocido como taxonomía, puede dificultar la mantenibilidad y comprensión del código si no se gestiona adecuadamente.
Ejemplo de herencia
classSuperClase:defmetodo_original(self):return"Método de la superclase"classSubClase(SuperClase):defnuevo_metodo(self):return"Método extendido de la subclase"
En este ejemplo, SubClase hereda de SuperClase y extiende su comportamiento añadiendo nuevo_metodo.
¿Por qué elegir la composición sobre la herencia?
Existe un conocido principio de diseño que recomienda optar por la composición antes que por la herencia. ¿Por qué? Este enfoque puede resultar en soluciones más flexibles y menos acopladas al evitar jerarquías de herencia innecesariamente complejas.
¿Cómo definimos la composición?
La composición se refiere a la relación entre dos clases donde una clase contiene objetos de otra clase dentro de sus propiedades. Es como decir que una clase está "compuesta" por diferentes componentes más pequeños.
Ejemplo de composición
Consideremos el ejemplo de un carro:
classEngine:defstart(self):return"El motor ha arrancado"classCar:def__init__(self, engine): self.engine = engine
defmove(self):return self.engine.start()
En este caso, Car no hereda de Engine, sino que contiene un objeto de Engine como una de sus propiedades. Esto permite que Car utilice el método start del motor sin necesidad de heredar de la clase Engine.
Ventajas de la composición
Flexibilidad: Permite modificar o añadir funcionalidades sin alterar las clases existentes.
Desacoplamiento: Reduce el acoplamiento entre clases, lo que mejora la mantenibilidad del código.
Reflexiones finales: Elegir herencia o composición
No siempre es blanco y negro. La elección entre herencia y composición depende de la solución específica que se quiera implementar. Es esencial considerar el contexto del problema al decidir cuál usar. Aunque la composición ofrece varias ventajas, la herencia sigue siendo una herramienta valiosa cuando se utiliza con criterio y conocimiento.
La clave está en entender ambos conceptos, sus ventajas y también sus limitaciones. Así, podrás aplicar un enfoque versátil y adaptado a las necesidades de tu proyecto. ¡Sigue explorando y aprendiendo para desarrollar un código más robusto y eficiente!
El uso de los utility types en TS es altamente recomendado, te dan limpieza, agregan legibilidad, practicidad y lo mejor de todo resuabilidad (tambien extensibilidad, pero me parece más claro lo primero)
En la programación orientada a objetos (POO), tanto la composición como la agregación son formas de asociación que describen relaciones entre clases. Sin embargo, tienen diferencias importantes en cuanto a la vida útil de los objetos involucrados:
Agregación: Es una relación "tiene-un" donde los componentes pueden existir independientemente. Por ejemplo, un equipo de fútbol tiene jugadores; si se disuelve el equipo, los jugadores siguen existiendo.
Composición: Es una relación "tiene-un" donde los componentes no pueden existir sin el conjunto. Por ejemplo, un libro tiene páginas; si se destruye el libro, las páginas también dejan de existir.
Actualmente ya uso composicion para mis componentes en React y es muy util. Hay que ver como se comporta en escenarios como una API con sus servicios que manejen Clases/Modelos/Tablas
Si entiendo bien, "composición" puede inferirse como el uso de un objeto o clase(s) para definir propiedades de otra clase. Esto no iría en contra de los principios SOLID tan recomendados para el manejo de clases?
Los principios SOLID son una guia que tiene su origen en la POO, la composición proviene del algebra y habla sobre la relacion que existe entre la aplicacion sucesiva de funciones matematicas, el famoso gof y fog (https://es.wikipedia.org/wiki/Funci%C3%B3n_compuesta).
La forma que me parece mas adecuada de definir la composicion en terminos de la programacion no es desde la optica de definir propiedades, sino de entender como las estructuras grandes estan compuestas de las mas chicas, ej. Un carro esta compuesto de un motor, el carro tiene su comportamiento unico, así como el motor, sin embargo existe una relacion entre estos, el carro esta compuesto del motor y no puede funcionar sin el.
Visto así, que principio SOLID, consideras John que no se esta cumpliendo, sabiendo que estos se enfocan mas en la implementacion de la solucion y no en la definicion de las reglas de relacion entre los entes abstractos (el carro y el motor)?
Debes usar la herencia exclusivamente cuando exista una relación estricta de "es un" (por ejemplo, un Perroes unAnimal). Es la herramienta perfecta cuando necesitas aplicar polimorfismo, es decir, cuando quieres que diferentes objetos respondan al mismo método de forma distinta, pero compartiendo una base lógica idéntica.
Si solo quieres reutilizar un fragmento de código o una función específica, la herencia es el camino equivocado. Utilízala cuando estés absolutamente seguro de que la subclase necesitará exponer toda la interfaz pública de la superclase y cuando el dominio del problema dicte que el comportamiento base no cambiará drásticamente en el futuro. Es un contrato fuerte, así que úsalo solo cuando la relación sea inquebrantable.
Con el ejemplo que dio sobre motor, y el carro, si bien hace referencia a conceptos parecidos con extensibilidad, no podria referirse tambien a la abstraccion?
Totalmente Karen, la abstraccion es el proceso que nos hara entender el problema, hallar los elementos de nuestro negocio que seran traducidos a una solucion a nivel tecnologico (codigo, servicios en la nube, etc).
Se menciona la composición, pero, ¿y donde queda el concepto de agregación? pues la composición y la agregación son dos cosas que me llegan a confundir al intentar identificar cuando están presentes estas relaciones entre clases y sobretodo al modelarla en UML.
La composición es un tipo de relación fuerte, como el ejemplo que se muestra en la clase. En cambio la agregación es un tipo de relación débil. Una pregunta que nos podemos hacer para decidir en un esquema de UML si la relación entre clases es de composición o agregación, es: si se elimina un objeto de esa clase, el objeto relacionado sigue existiendo? En caso de que sea así la relación es débil, y hablamos de una agregación. En caso contrario es una composición.
Me permito agregar lo siguiente: Tienes un carro, en la linea de producion se le AGREGAN llantas, motor, cableria, todos estos ya creados previamente.
En codigo: Al crear un objeto de la clase carro, le paso al metodo constructor los objetos ya creados de las clases motor y ruedas.
Otro caso seria que dentro de la linea de produccion del carro tambien se creara el motor, en este caso la linea del carro, ademas de crear el motor, tambien se encarga del motor.
En código: Creo un objeto de la clase carro y dentro del metodo constructor, creo un objeto de la clase motor. Ya no se lo paso como parametro, sino que lo creo internamente.
Ambos tienen utilidad, ambos dependeran de lo que desees modelar del mundo real
Gracias por la respuesta. Supongo que lo veo más desde la implementación como tal. Para el ejemplo, visualizaba la implementación de carro algo así como:
Y es en esa creación de motor donde me queda el conflicto de si tal aproximación cumple con los principios de independencia. Más adelante en el curso vi que los patrones Factory hacen precisamente algo similar, crear directamente el objeto "propiedad" (motor) dentro de la sueprclase (carro). Pero entonces, si es correcto este tipo de implementación?