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.
Desafio
FácilVamos 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úblicaCPUcom dois campos públicos:cores(u32) para o número de núcleos, espeed_ghz(f32) para a velocidade do clock. Adicione um métodospecsque retorna uma String descrevendo a CPU no formato"{cores}-core @ {speed_ghz}GHz".memory.rs: Defina uma struct públicaMemorycom um campo públicosize_gb(u32) para o tamanho da memória. Adicione um métodospecsque retorna uma String no formato"{size_gb}GB RAM".main.rs: Importe ambos os módulos e crie uma struct públicaComputerque possui um campocpudo tipoCPUe um campomemorydo tipoMemory. Implemente um métodosystem_infono 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 RAME com as entradas 4, 2.8, e 32:
System: 4-core @ 2.8GHz | 32GB RAMVocê 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).
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
}
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
1Métodos e Comportamento
Introdução a Blocos de ImplementaçãoO Parâmetro SelfMétodos MutáveisFunções AssociadasMúltiplos Blocos de ImplementaçãoEncadeamento de MétodosRecapitulação - Ações de Retângulo4Projeto: Pet Virtual
Definindo o PetAlimentando o Pet7Traits Padrão
A Trait DebugA Trait DisplayClone e CopyTraits de IgualdadeRecapitulação - Ponto Imprimível10Projeto: Sistema de Documentos
A Trait DrawComponente de Texto2Encapsulamento e Módulos
Fundamentos de MódulosA Palavra-chave PublicCampos PrivadosGettersSettersRecapitulação - Cofre Seguro5Genéricos
Structs GenéricasMétodos GenéricosMúltiplos Tipos GenéricosFunções GenéricasRecapitulação - Ponto de Coordenada8Traits como Restrições
Sintaxe de Restrições de TraitRestrições MúltiplasA Cláusula WhereRetornando Tipos com TraitsRecapitulação - Impressora Genérica11Padrões de Projeto em Rust
Padrão NewtypeComposiçãoA Trait DropFrom e IntoRecapitulação - Mock de Smart Pointer3Enums Avançados
Enums com DadosMétodos em EnumsCorrespondência de Variantes de DadosO Enum Option RevisitadoRecapitulação - Enum Shape6Definição de Traits
O que é uma Trait?Implementando TraitsImplementações PadrãoSobrescrevendo PadrõesTraits com ParâmetrosRecapitulação - Media Player