Menu
Coddy logo textTech

Composição vs Herança

Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy. Lição 47 de 87.

A herança cria uma relação de "é um": um Dog é um Animal. Mas e se uma classe precisar da funcionalidade de algo que ela não é? É aí que entra a composição, estabelecendo, em vez disso, uma relação de "tem um".

Com composição, uma classe contém instâncias de outras classes como campos, em vez de estendê-las:

// Abordagem de herança: Car É um Engine (não faz sentido)
class Car extends Engine { }

// Abordagem de composição: Car TEM um Engine (faz sentido)
class Car {
    private Engine engine;
    
    public Car(Engine engine) {
        this.engine = engine;
    }
    
    public void start() {
        engine.ignite();
    }
}

A composição oferece várias vantagens. Você pode alterar o objeto composto em tempo de execução, como trocar engines.

Você também pode combinar comportamentos de várias classes, algo que a herança não permite em Java. A classe composta expõe apenas os métodos que escolhe, proporcionando um encapsulamento melhor.

Uma diretriz prática: use herança quando houver uma relação genuína de "é um" e a subclasse realmente representar uma versão especializada da classe pai.

Use composição quando precisar usar a funcionalidade de outra classe sem ser desse tipo. Muitos desenvolvedores experientes seguem o princípio: "prefira composição à herança", porque isso resulta em um código mais flexível e fácil de manter.

challenge icon

Desafio

Fácil

Vamos construir um sistema de computador usando composição para demonstrar a relação "tem-um". Em vez de um Computer herdar de seus componentes (o que não faria sentido), seu computador conterá as partes necessárias para funcionar.

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

  • Processor.java: Crie uma classe que represente uma CPU. Um Processor tem dois campos privados: brand (String) e speedGHz (double). Inclua um construtor para inicializar ambos os campos e métodos getter para cada um. Adicione um método process() que retorne uma String: [brand] processing at [speedGHz] GHz
  • Memory.java: Crie uma classe que represente a RAM. Uma Memory tem dois campos privados: type (String, como "DDR4" ou "DDR5") e sizeGB (int). Inclua um construtor, getters e um método load() que retorne: Loading [sizeGB]GB [type] memory
  • Computer.java: É aqui que a composição se destaca! Sua classe Computer deve ter um Processor e uma Memory como campos privados. Ela não os estende; ela os contém. Inclua um construtor que aceite ambos os componentes. Adicione um método boot() que retorne uma String de várias linhas mostrando o computador inicializando por meio de seus componentes:
    Booting computer...
    [processor.process() result]
    [memory.load() result]
    System ready!
    Adicione também um método getSpecs() que retorne: Specs: [processor brand] CPU, [memory sizeGB]GB [memory type]
  • Main.java: Dê vida ao seu sistema composto! Você receberá quatro entradas: a marca do processador (String), a velocidade do processador (double), o tipo de memória (String) e o tamanho da memória (int).

    Crie um Processor e uma Memory com esses valores e, em seguida, componha-os em um Computer. Imprima o resultado da chamada de boot() e, depois, imprima o resultado de getSpecs().

Você receberá quatro entradas, nesta ordem: marca do processador, velocidade do processador (GHz), tipo de memória e tamanho da memória (GB).

Observe como o Computer delega o trabalho aos seus componentes em vez de tentar ser esses componentes. Esse é o poder da composição! O Computer pode usar qualquer Processor ou Memory que você fornecer, tornando o design flexível e realista.

Experimente você mesmo

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Ler entradas
        String processorBrand = scanner.nextLine();
        double processorSpeed = Double.parseDouble(scanner.nextLine());
        String memoryType = scanner.nextLine();
        int memorySize = Integer.parseInt(scanner.nextLine());
        
        // TODO: Crie um Processor com a marca e a velocidade
        
        // TODO: Crie uma Memory com o tipo e o tamanho
        
        // TODO: Crie um Computer compondo o Processor e a Memory
        
        // TODO: Imprima o resultado de boot()
        
        // TODO: Imprima o resultado de getSpecs()
    }
}
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 Programação Orientada a Objetos

Pratique por conta própria: Compilador de Java online