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.
Desafio
FácilVamos 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) espeedGHz(double). Inclua um construtor para inicializar ambos os campos e métodos getter para cada um. Adicione um métodoprocess()que retorne uma String:[brand] processing at [speedGHz] GHzMemory.java: Crie uma classe que represente a RAM. Uma Memory tem dois campos privados:type(String, como "DDR4" ou "DDR5") esizeGB(int). Inclua um construtor, getters e um métodoload()que retorne:Loading [sizeGB]GB [type] memoryComputer.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étodoboot()que retorne uma String de várias linhas mostrando o computador inicializando por meio de seus componentes:Adicione também um métodoBooting computer... [processor.process() result] [memory.load() result] System ready!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 degetSpecs().
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()
}
}
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
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão IteratorPratique por conta própria: Compilador de Java online