Menu
Coddy logo textTech

Composição

Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 55 de 61.

Muitas linguagens orientadas a objetos dependem fortemente da herança para compartilhar funcionalidades entre tipos. Rust adota uma abordagem diferente: composição. Em vez de herdar o comportamento de um pai, você constrói tipos complexos incluindo outros tipos como campos.

Considere modelar um carro. Em vez de criar uma classe base Vehicle e herdar dela, você compõe um Car a partir de componentes menores e focados:

struct Engine {
    horsepower: u32,
}

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

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

O Car não herda de Engine ou Wheels—ele os tem. Esta relação "tem-um" é composição. Você acessa os componentes internos através de seus campos e pode chamar seus métodos diretamente:

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

impl Car {
    fn start(&self) {
        self.engine.start();  // Delega para o componente interno
    }
}

Este padrão mantém cada struct focada em uma única responsabilidade. Precisa alterar como os motores funcionam? Modifique apenas a struct Engine. Quer reutilizar Wheels para uma motocicleta? Basta incluí-la em uma nova struct. A composição oferece flexibilidade sem o acoplamento rígido que a herança costuma criar.

challenge icon

Desafio

Fácil

Vamos construir um sistema de computador usando composição! Em vez de criar uma única struct massiva, você comporá um Computer a partir de componentes menores e focados — um CPU e uma Memory. Cada componente saberá como se descrever, e o Computer delegará as tarefas para suas partes.

Você organizará seu código em três arquivos:

  • cpu.rs: Defina uma struct pública CPU com dois campos públicos: cores (u32) para o número de núcleos, e speed_ghz (f32) para a velocidade do clock. Adicione um método specs que retorna uma String descrevendo a CPU no formato "{cores}-core @ {speed_ghz}GHz".
  • memory.rs: Defina uma struct pública Memory com um campo público size_gb (u32) para o tamanho da memória. Adicione um método specs que retorna uma String no formato "{size_gb}GB RAM".
  • main.rs: Importe ambos os módulos e crie uma struct pública Computer que possui um campo cpu do tipo CPU e um campo memory do tipo Memory. Implemente um método system_info no Computer que delega para seus componentes e imprime a especificação completa do sistema. Use as entradas fornecidas para construir um Computer e exibir suas informações.

O método system_info deve imprimir:

System: {cpu_specs} | {memory_specs}

Por exemplo, com as entradas 8, 3.5, e 16:

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

E com as entradas 4, 2.8, e 32:

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

Você receberá três entradas: núcleos da CPU (analise como u32), velocidade da CPU (analise como f32) e tamanho da memória (analise como u32).

Folha de consulta

Rust usa composição em vez de herança para compartilhar funcionalidade entre tipos. Você constrói tipos complexos incluindo outros tipos como campos, criando uma relação de "tem um" (has-a).

Composição Básica

Defina structs menores e focadas e componha-as em structs maiores:

struct Engine {
    horsepower: u32,
}

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

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

Delegando para Componentes

Acesse componentes internos através de seus campos e chame seus 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
    }
}

A composição mantém cada struct focada em uma única responsabilidade, proporcionando flexibilidade sem acoplamento forte.

Experimente você mesmo

mod cpu;
mod memory;

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

// TODO: Defina uma struct Computer pública com dois campos públicos:
// - cpu: CPU
// - memory: Memory

// TODO: Implemente um método system_info em Computer que:
// - Delega para cpu.specs() e memory.specs()
// - Imprime: 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: Crie instâncias de CPU e Memory usando as entradas processadas
    
    // TODO: Crie uma instância de Computer com a CPU e a Memory
    
    // TODO: Chame system_info() no computador para imprimir o resultado
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming