Implementando vs Estendendo
Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 47 de 110.
Agora que você entende tanto o extends quanto o implements, vamos esclarecer quando usar cada um.
Use extends quando você quiser herdar comportamento. Sua subclasse recebe todo o código do pai gratuitamente e pode construir sobre ele:
class Animal {
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
void bark() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.breathe(); // Herdado - funciona automaticamente
dog.bark();
}Use implements quando você deseja garantir uma estrutura sem herdar código. Você deve escrever cada método por conta própria:
class Robot implements Animal {
@override
void breathe() {
print('Ventilating circuits...'); // Implementação completamente personalizada
}
}A principal diferença: extends diz "Eu sou um tipo disso" enquanto implements diz "Eu posso fazer o que isso faz." Um Dog é um Animal e respira da mesma forma. Um Robot não é um animal, mas pode cumprir o mesmo contrato com sua própria lógica.
Lembre-se: você só pode estender uma classe, mas pode implementar muitas interfaces. Escolha extends para relacionamentos de herança verdadeiros, e implements quando você precisar de flexibilidade ou quiser impor um contrato sem compartilhar detalhes de implementação.
Desafio
FácilVamos construir um sistema de trabalhadores que demonstra quando usar extends versus implements. Você criará uma classe base de trabalhador e, em seguida, mostrará ambas as abordagens: uma classe que verdadeiramente "é um" trabalhador e herda o comportamento, e outra que simplesmente "pode fazer" o que um trabalhador faz, mas com uma lógica completamente personalizada.
Você organizará seu código em dois arquivos:
worker.dart: Defina sua hierarquia de trabalhadores aqui:- Uma classe
Workercom uma propriedadeString namee um construtor. Inclua um métodowork()que imprime[name] is working...e um métodotakeBreak()que imprime[name] takes a coffee break - Uma classe
OfficeWorkerque estende (extends)Worker. Adicione uma propriedadeString department. Sobrescrevawork()para imprimir[name] is typing reports in [department]mas mantenha o comportamento herdado detakeBreak() - Uma classe
RobotWorkerque implementa (implements)Worker. Adicione uma propriedadeString model. Como robôs não são realmente trabalhadores, mas podem fazer o que trabalhadores fazem, forneça implementações completamente personalizadas:work()deve imprimirRobot [model] is assembling partsetakeBreak()deve imprimirRobot [model] is recharging
- Uma classe
main.dart: Importe seu arquivo de trabalhador e demonstre ambas as abordagens:- Crie um
OfficeWorkerchamado'Alice'no departamento'Marketing' - Crie um
RobotWorkercom o modelo'RX-7' - Para o trabalhador de escritório, chame
work()e depoistakeBreak() - Imprima uma linha vazia
- Para o robô trabalhador, chame
work()e depoistakeBreak()
- Crie um
Observe como OfficeWorker herda takeBreak() automaticamente porque estende Worker - ele verdadeiramente "é um" trabalhador. Enquanto isso, o RobotWorker deve fornecer sua própria implementação para tudo porque ele apenas implementa a interface - ele "pode fazer" o que um trabalhador faz, mas do seu próprio jeito robótico.
Saída esperada:
Alice is typing reports in Marketing
Alice takes a coffee break
Robot RX-7 is assembling parts
Robot RX-7 is rechargingFolha de consulta
Use extends para herdar comportamento de uma classe pai. A subclasse recebe automaticamente todos os métodos do pai e pode sobrescrevê-los:
class Animal {
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
void bark() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.breathe(); // Herdado automaticamente
dog.bark();
}Use implements para garantir uma estrutura sem herdar código. Você deve fornecer sua própria implementação para cada método:
class Robot implements Animal {
@override
void breathe() {
print('Ventilating circuits...');
}
}Principais diferenças:
extends: "Eu sou um tipo disso" - herança verdadeira com código compartilhadoimplements: "Eu posso fazer o que isso faz" - impõe um contrato com lógica personalizada- Você só pode estender uma classe, mas pode implementar múltiplas interfaces
Escolha extends para relacionamentos pai-filho verdadeiros onde o comportamento deve ser herdado. Escolha implements quando precisar de flexibilidade ou quiser impor um contrato sem compartilhar a implementação.
Experimente você mesmo
import 'worker.dart';
void main() {
// TODO: Crie um OfficeWorker chamado 'Alice' no departamento 'Marketing'
// TODO: Crie um RobotWorker com o modelo 'RX-7'
// TODO: Para o trabalhador de escritório, chame work() e depois takeBreak()
// TODO: Imprima uma linha vazia
// TODO: Para o trabalhador robô, chame work() e depois takeBreak()
}
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 ExternosBibliotecas e ImportaçõesIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosVariáveis de InstânciaFundamentos de ConstrutoresRecapitulação - Calculadora Simples4Null Safety
Introdução ao Null SafetyNullable vs Non-NullableOs Operadores ? e !Palavra-chave Late e Null SafetyOperadores Null-AwareNull Safety em ClassesRecapitulação - Sistema de Perfil de Usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementando vs EstendendoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Generics
Visão Geral de List, Set e MapColeções Type-SafeClasses GenéricasMétodos GenéricosRestrições de GenericsIterable e IteratorRecapitulação - Armazenamento Genérico13Conceitos Avançados de POO
Composição vs HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário2Construtores em Dart
Construtor PadrãoConstrutores NomeadosListas de InicializaçãoConstrutores ConstantesConstrutores FactoryConstrutores de RedirecionamentoRecapitulação - Shape Builder5Encapsulamento
Membros Públicos vs PrivadosA Convenção do Prefixo _Privacidade em Nível de BibliotecaAprofundamento em Getters & SettersOcultação de InformaçãoRecapitulação - Registros de Estudantes8Mixins
Introdução aos MixinsCriando MixinsUsando Múltiplos MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema Animal11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode & ==Interface ComparableMétodo call()Sobrescrita de noSuchMethodRecapitulação - Coleção Personalizada14Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy3Propriedades de Classe
Membros de Instância vs EstáticosCampos Final & ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Conta Bancária6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodoA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do PolimorfismoPolimorfismo via InterfacesVerificação de Tipos em Tempo de ExecuçãoOs Operadores is & asPalavra-chave CovariantRecapitulação - Processador de Pagamentos12POO Assíncrona
Futures e async/awaitFundamentos de StreamsStream ControllersConstrutores AssíncronosAsync em Métodos de ClasseRecapitulação - Data Fetcher15Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository