Menu
Coddy logo textTech

Composición

Parte de la sección Object Oriented Programming del Journey de Rust de Coddy — lección 55 de 61.

Muchos lenguajes orientados a objetos dependen en gran medida de la herencia para compartir funcionalidad entre tipos. Rust adopta un enfoque diferente: composición. En lugar de heredar el comportamiento de un padre, construyes tipos complejos incluyendo otros tipos como campos.

Considera modelar un coche. En lugar de crear una clase base Vehicle y heredar de ella, compones un Car a partir de componentes más pequeños y enfocados:

struct Engine {
    horsepower: u32,
}

struct Wheels {
    count: u8,
    diameter: f32,
}

struct Car {
    engine: Engine,
    wheels: Wheels,
    brand: String,
}

El Car no hereda de Engine o Wheels; los tiene. Esta relación "tiene-un" es composición. Accedes a los componentes internos a través de sus campos y puedes llamar a sus métodos directamente:

impl Engine {
    fn start(&self) {
        println!("Engine with {} HP started!", self.horsepower);
    }
}

impl Car {
    fn start(&self) {
        self.engine.start();  // Delegar al componente interno
    }
}

Este patrón mantiene cada struct enfocado en una sola responsabilidad. ¿Necesitas cambiar cómo funcionan los motores? Modifica solo el struct Engine. ¿Quieres reutilizar Wheels para una motocicleta? Simplemente inclúyelo en un nuevo struct. La composición te brinda flexibilidad sin el acoplamiento fuerte que la herencia suele crear.

challenge icon

Desafío

Fácil

¡Vamos a construir un sistema informático utilizando composición! En lugar de crear una única estructura masiva, compondrás un Computer a partir de componentes más pequeños y enfocados: un CPU y Memory. Cada componente sabrá cómo describirse a sí mismo, y el Computer delegará en sus partes.

Organizarás tu código en tres archivos:

  • cpu.rs: Define una estructura CPU pública con dos campos públicos: cores (u32) para el número de núcleos, y speed_ghz (f32) para la velocidad de reloj. Añade un método specs que devuelva un String describiendo la CPU en el formato "{cores}-core @ {speed_ghz}GHz".
  • memory.rs: Define una estructura Memory pública con un campo público size_gb (u32) para el tamaño de la memoria. Añade un método specs que devuelva un String en el formato "{size_gb}GB RAM".
  • main.rs: Importa ambos módulos y crea una estructura Computer pública que tenga un campo cpu de tipo CPU y un campo memory de tipo Memory. Implementa un método system_info en Computer que delegue en sus componentes e imprima la especificación completa del sistema. Utiliza las entradas proporcionadas para construir un Computer y mostrar su información.

El método system_info debe imprimir:

System: {cpu_specs} | {memory_specs}

Por ejemplo, con las entradas 8, 3.5, y 16:

System: 8-core @ 3.5GHz | 16GB RAM

Y con las entradas 4, 2.8, y 32:

System: 4-core @ 2.8GHz | 32GB RAM

Recibirás tres entradas: núcleos de CPU (analizar como u32), velocidad de CPU (analizar como f32) y tamaño de memoria (analizar como u32).

Hoja de referencia

Rust utiliza la composición en lugar de la herencia para compartir funcionalidad entre tipos. Se construyen tipos complejos incluyendo otros tipos como campos, creando una relación de tipo "tiene un" (has-a).

Composición Básica

Define structs más pequeños y enfocados, y composiciónalos en otros más grandes:

struct Engine {
    horsepower: u32,
}

struct Wheels {
    count: u8,
    diameter: f32,
}

struct Car {
    engine: Engine,
    wheels: Wheels,
    brand: String,
}

Delegación a Componentes

Accede a los componentes internos a través de sus campos y llama a sus métodos:

impl Engine {
    fn start(&self) {
        println!("Engine with {} HP started!", self.horsepower);
    }
}

impl Car {
    fn start(&self) {
        self.engine.start();  // Delegate to the inner component
    }
}

La composición mantiene cada struct enfocado en una sola responsabilidad, proporcionando flexibilidad sin un acoplamiento fuerte.

Pruébalo tú mismo

mod cpu;
mod memory;

use cpu::CPU;
use memory::Memory;

// TODO: Define una struct Computer pública con dos campos públicos:
// - cpu: CPU
// - memory: Memory

// TODO: Implementa un método system_info en Computer que:
// - Delegue a cpu.specs() y memory.specs()
// - Imprima: System: {cpu_specs} | {memory_specs}

fn main() {
    let mut input = String::new();
    std::io::stdin().read_line(&mut input).expect("Failed to read line");
    let cores: u32 = input.trim().parse().expect("Invalid number");
    
    input.clear();
    std::io::stdin().read_line(&mut input).expect("Failed to read line");
    let speed_ghz: f32 = input.trim().parse().expect("Invalid number");
    
    input.clear();
    std::io::stdin().read_line(&mut input).expect("Failed to read line");
    let size_gb: u32 = input.trim().parse().expect("Invalid number");
    
    // TODO: Crea instancias de CPU y Memory usando las entradas procesadas
    
    // TODO: Crea una instancia de Computer con la CPU y la Memory
    
    // TODO: Llama a system_info() en el computer para imprimir el resultado
}
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 Object Oriented Programming