Menu
Coddy logo textTech

Composición vs. Herencia

Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy. Lección 47 de 87.

La herencia crea una relación «es-un»: un Dog es un Animal. Pero ¿qué ocurre si una clase necesita funcionalidad de algo que no es? Aquí es donde entra en juego la composición, que establece una relación «tiene-un» en su lugar.

Con la composición, una clase contiene instancias de otras clases como campos en lugar de extenderlas:

// Enfoque de herencia: Car ES un Engine (no tiene sentido)
class Car extends Engine { }

// Enfoque de composición: Car TIENE un Engine (tiene sentido)
class Car {
    private Engine engine;
    
    public Car(Engine engine) {
        this.engine = engine;
    }
    
    public void start() {
        engine.ignite();
    }
}

La composición ofrece varias ventajas. Puedes cambiar el objeto compuesto durante la ejecución, como intercambiar engines.

También puedes combinar comportamientos de varias clases, algo que la herencia no permite en Java. La clase compuesta solo expone los métodos que elige, lo que proporciona un mejor encapsulamiento.

Una pauta práctica: usa la herencia cuando exista una relación genuina de «es un» y la subclase represente realmente una versión especializada de la clase principal.

Usa la composición cuando necesites utilizar la funcionalidad de otra clase sin ser de ese tipo. Muchos desarrolladores con experiencia siguen el principio: «prefiere la composición a la herencia», porque da lugar a un código más flexible y fácil de mantener.

challenge icon

Desafío

Fácil

Construyamos un sistema informático usando composición para demostrar la relación «tiene un». En lugar de que un Computer herede de sus componentes (lo cual no tendría sentido), tu computadora contendrá las partes que necesita para funcionar.

Organizarás tu código en tres archivos:

  • Processor.java: Crea una clase que represente una CPU. Un Processor tiene dos campos privados: brand (String) y speedGHz (double). Incluye un constructor para inicializar ambos campos y métodos getter para cada uno. Añade un método process() que devuelva un String: [brand] processing at [speedGHz] GHz
  • Memory.java: Crea una clase que represente la RAM. Un Memory tiene dos campos privados: type (String, como "DDR4" o "DDR5") y sizeGB (int). Incluye un constructor, getters y un método load() que devuelva: Loading [sizeGB]GB [type] memory
  • Computer.java: ¡Aquí es donde destaca la composición! Tu clase Computer debe tener un Processor y un Memory como campos privados. No los extiende, los contiene. Incluye un constructor que acepte ambos componentes. Añade un método boot() que devuelva un String de varias líneas mostrando cómo la computadora se inicia utilizando sus componentes:
    Booting computer...
    [processor.process() result]
    [memory.load() result]
    System ready!
    Añade también un método getSpecs() que devuelva: Specs: [processor brand] CPU, [memory sizeGB]GB [memory type]
  • Main.java: ¡Da vida a tu sistema compuesto! Recibirás cuatro entradas: marca del procesador (String), velocidad del procesador (double), tipo de memoria (String) y tamaño de la memoria (int).

    Crea un Processor y un Memory con estos valores y, después, compónlos en un Computer. Imprime el resultado de llamar a boot() y, después, imprime el resultado de getSpecs().

Recibirás cuatro entradas en este orden: marca del procesador, velocidad del procesador (GHz), tipo de memoria y tamaño de la memoria (GB).

Observa cómo el Computer delega el trabajo en sus componentes en lugar de intentar serlos. ¡Este es el poder de la composición! El Computer puede utilizar cualquier Processor o Memory que le proporciones, lo que hace que el diseño sea flexible y realista.

Pruébalo tú mismo

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Leer entradas
        String processorBrand = scanner.nextLine();
        double processorSpeed = Double.parseDouble(scanner.nextLine());
        String memoryType = scanner.nextLine();
        int memorySize = Integer.parseInt(scanner.nextLine());
        
        // TODO: Crea un Processor con la marca y la velocidad
        
        // TODO: Crea una Memory con el tipo y el tamaño
        
        // TODO: Crea un Computer componiendo el Processor y la Memory
        
        // TODO: Imprime el resultado de boot()
        
        // TODO: Imprime el resultado de getSpecs()
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de Java online