Mixin vs Herança
Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 54 de 110.
Agora que você aprendeu como os mixins funcionam, vamos esclarecer quando usá-los em vez de herança. A principal diferença reside na relação que eles expressam.
Herança representa uma relação "é-um". Um Dog é um Animal. A subclasse é uma versão especializada da classe pai, e você só pode estender uma única classe.
Mixins, por outro lado, representam uma relação "pode-fazer". Um Dog pode nadar, mas nadar não é o que define um cachorro.
// Herança: Dog É um Animal
class Animal {
String name;
Animal(this.name);
}
class Dog extends Animal {
Dog(String name) : super(name);
}
// Mixin: Dog PODE nadar (mas não é definido pelo ato de nadar)
mixin Swimming {
void swim() => print('Swimming');
}
class Dog extends Animal with Swimming {
Dog(String name) : super(name);
}Use herança quando as classes compartilham uma identidade fundamental e você precisa de construtores na classe pai. Use mixins quando quiser adicionar funcionalidades a classes que não compartilham um ancestral comum.
| Recurso | Herança | Mixin |
|---|---|---|
| Relacionamento | é-um | pode-fazer |
| Limite | Apenas um pai | Múltiplos mixins |
| Construtores | Permitido | Não permitido |
| Caso de uso | Especialização | Comportamento compartilhado |
A melhor parte? Você pode combinar ambos - estender uma classe e incorporar múltiplas capacidades, dando às suas classes exatamente os recursos de que precisam.
Desafio
FácilVamos construir um sistema de trabalhadores que demonstra quando usar herança versus mixins. Você criará uma classe base para o relacionamento "é um" (is-a) e mixins para as capacidades "pode fazer" (can-do), e então os combinará para mostrar como ambas as abordagens funcionam juntas.
Você organizará seu código em três arquivos:
worker.dart: Defina sua classe base que estabelece a identidade central de todos os trabalhadores:- Uma classe
Workercom as propriedadesString nameeString department. Inclua um construtor que receba ambos os valores. Adicione um métodointroduce()que imprime'$name works in $department'
- Uma classe
skills.dart: Defina mixins que representam capacidades que os trabalhadores podem ter (os relacionamentos "pode fazer"):- Um mixin
CanCodecom um métodowriteCode()que imprime'Writing code...' - Um mixin
CanDesigncom um métodocreateDesign()que imprime'Creating design...' - Um mixin
CanManagecom um métodoleadTeam()que imprime'Leading the team...'
- Um mixin
main.dart: Importe ambos os arquivos e crie tipos de trabalhadores especializados que combinam herança com mixins:- Uma classe
Developerque estendeWorkere usa o mixinCanCode - Uma classe
TechLeadque estendeWorkere usa os mixinsCanCodeeCanManage - Crie um
Developerchamado'Alice'no departamento'Engineering' - Crie um
TechLeadchamado'Bob'no departamento'Engineering' - Para Alice: chame
introduce()e depoiswriteCode() - Imprima uma linha vazia
- Para Bob: chame
introduce(), depoiswriteCode(), e entãoleadTeam()
- Uma classe
Observe como tanto Developer quanto TechLead são trabalhadores (herança), mas eles podem fazer coisas diferentes (mixins). O TechLead combina múltiplas capacidades sem precisar de herança múltipla.
Saída esperada:
Alice works in Engineering
Writing code...
Bob works in Engineering
Writing code...
Leading the team...Folha de consulta
A herança representa um relacionamento "é um" (uma subclasse é uma versão especializada do pai), enquanto os mixins representam um relacionamento "pode fazer" (adicionando capacidades sem definir uma identidade).
// Inheritance: Dog IS an Animal
class Animal {
String name;
Animal(this.name);
}
class Dog extends Animal {
Dog(String name) : super(name);
}
// Mixin: Dog CAN swim
mixin Swimming {
void swim() => print('Swimming');
}
class Dog extends Animal with Swimming {
Dog(String name) : super(name);
}| Recurso | Herança | Mixin |
|---|---|---|
| Relacionamento | é um | pode fazer |
| Limite | Apenas um pai | Múltiplos mixins |
| Construtores | Permitido | Não permitido |
| Caso de uso | Especialização | Comportamento compartilhado |
Você pode combinar ambas as abordagens - estender uma classe e misturar múltiplas capacidades:
class TechLead extends Worker with CanCode, CanManage {
TechLead(String name, String department) : super(name, department);
}Experimente você mesmo
import 'worker.dart';
import 'skills.dart';
// TODO: Crie uma classe Developer que estende Worker e usa o mixin CanCode
// TODO: Crie uma classe TechLead que estende Worker e usa ambos os mixins CanCode e CanManage
void main() {
// TODO: Crie um Developer chamado 'Alice' no departamento 'Engineering'
// TODO: Chame introduce() e depois writeCode() para Alice
// TODO: Imprima uma linha vazia
// TODO: Crie um TechLead chamado 'Bob' no departamento 'Engineering'
// TODO: Chame introduce(), writeCode() e depois leadTeam() para Bob
}
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