Mixin vs Interface
Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy. Lição 55 de 110.
Você viu como os mixins se comparam à herança. Agora vamos comparar mixins com interfaces, já que ambos permitem que você trabalhe com múltiplos tipos. A principal diferença é o que eles fornecem: interfaces definem um contrato, enquanto mixins fornecem implementação.
Quando você implementa uma interface, promete fornecer determinados métodos, mas escreve todo o código por conta própria. Quando usa um mixin, obtém o código real gratuitamente.
// Abordagem de interface - você deve implementar tudo
abstract class Printable {
void printInfo();
}
class Report implements Printable {
@override
void printInfo() {
print('Printing report...'); // Você escreve isso
}
}
// Abordagem de mixin - implementação incluída
mixin Printable {
void printInfo() {
print('Printing...'); // Já escrito para você
}
}
class Report with Printable {} // Recebe printInfo() automaticamenteEntão, quando você deve usar cada um? Use uma interface quando classes diferentes precisarem das mesmas assinaturas de método, mas de implementações diferentes. Use um mixin quando as classes deverem compartilhar exatamente o mesmo comportamento.
| Recurso | Interface | Mixin |
|---|---|---|
| Fornece | Apenas o contrato | Contrato + código |
| Implementação | Você escreve | Incluída |
| Palavra-chave | implements | with |
| Ideal para | Comportamentos diferentes, mesma API | Comportamento compartilhado |
Você pode até combinar ambas as abordagens - use interfaces para definir o que uma classe pode fazer e mixins para fornecer implementações comuns onde for apropriado. No Dart, ao combinar ambos, sempre coloque with antes de implements:
class MyClass with SomeMixin implements SomeInterface {
// with vem antes de implements
}Desafio
FácilVamos criar um sistema de notificações que demonstra quando usar interfaces em vez de mixins. Você criará uma interface para notificações que exigem implementações diferentes e um mixin para o comportamento de registro compartilhado que deve ser idêntico em todos os tipos de notificação.
Você organizará seu código em três arquivos:
notifiable.dart: defina uma classe abstrataNotifiableque atua como uma interface. Ela deve declarar dois métodos abstratos:send(String message)egetChannel(), que retorna umaString. Diferentes tipos de notificação implementarão esses métodos de maneiras diferentes, com base em seu canal específico.logging.dart: crie um mixinLoggingque forneça o comportamento compartilhado que todas as notificações devem ter. Inclua um métodologSent(String channel, String message)que imprima[LOG] Sent via [channel]: [message]e um métodologTimestamp()que imprima[LOG] Timestamp recorded. Esse comportamento é idêntico para todos os tipos de notificação, o que o torna perfeito para um mixin.main.dart: importe os dois arquivos e crie duas classes de notificação que combinem a interface com o mixin:- Uma classe
EmailNotificationque use o mixinLogginge implementeNotifiable. SeugetChannel()deve retornar'Email', e seu métodosend()deve imprimirSending email: [message], depois chamarlogSent()com o canal e a mensagem e, em seguida, chamarlogTimestamp() - Uma classe
SMSNotificationque use o mixinLogginge implementeNotifiable. SeugetChannel()deve retornar'SMS', e seu métodosend()deve imprimirSending SMS: [message], depois chamarlogSent()com o canal e a mensagem e, em seguida, chamarlogTimestamp()
EmailNotificatione chamesend('Hello World'), imprima uma linha vazia, crie umSMSNotificatione chamesend('Quick update')- Uma classe
Observe como a interface força cada classe a escrever suas próprias implementações de send() e getChannel() (comportamento diferente), enquanto o mixin fornece os métodos de registro que funcionam de forma idêntica para ambas (comportamento compartilhado).
Saída esperada:
Sending email: Hello World
[LOG] Sent via Email: Hello World
[LOG] Timestamp recorded
Sending SMS: Quick update
[LOG] Sent via SMS: Quick update
[LOG] Timestamp recordedExperimente você mesmo
import 'notifiable.dart';
import 'logging.dart';
// TODO: Criar a classe EmailNotification que:
// - usa o mixin Logging (with)
// - implementa Notifiable (implements)
// - getChannel() retorna 'Email'
// - send() imprime 'Sending email: [message]', depois chama logSent(), depois logTimestamp()
// TODO: Criar a classe SMSNotification que:
// - usa o mixin Logging (with)
// - implementa Notifiable (implements)
// - getChannel() retorna 'SMS'
// - send() imprime 'Sending SMS: [message]', depois chama logSent(), depois logTimestamp()
void main() {
// TODO: Criar uma EmailNotification e chamar send('Hello World')
// TODO: Imprimir uma linha vazia
// TODO: Criar uma SMSNotification e chamar send('Quick update')
}
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 OOP
Arquivos externosBibliotecas e importsIntrodução à OOPClasses vs. objetosA palavra-chave thisMétodosVariáveis de instânciaNoções básicas de construtoresRevisão - Calculadora simples4Segurança contra nulos
Introdução à segurança contra nulosAnulável vs. não anulávelOs operadores ? e !Palavra-chave Late e segurança contra nulosOperadores conscientes de nulosSegurança contra nulos em classesRecapitulação — Sistema de perfil de usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementação vs. ExtensãoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Genéricos
Visão geral de List, Set e MapColeções com segurança de tiposClasses genéricasMétodos genéricosRestrições genéricasIterable e IteratorRecapitulação - Armazenamento genérico13Conceitos Avançados de OOP
Composição vs. HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Registros (Dart 3)Padrões e Correspondência (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses de Livros e Usuários2Construtores em Dart
Construtor padrãoConstrutores nomeadosListas de inicializaçãoConstrutores constantesConstrutores factoryConstrutores de redirecionamentoRevisão - Construtor de formas5Encapsulamento
Membros Públicos vs. PrivadosConvenção do Prefixo _Privacidade no Nível da BibliotecaAprofundamento em Getters e SettersOcultação de InformaçõesRecapitulação - Registros de Alunos8Mixins
Introdução a MixinsCriando MixinsUsando vários MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema de Animais11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode e ==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 e ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Contas Bancárias6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodosA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do polimorfismoPolimorfismo por meio de interfacesVerificação de tipos em tempo de execuçãoOs operadores is e asPalavra-chave covarianteRecapitulação — Processador de pagamentos12OOP Assíncrona
Futures e async/awaitNoções básicas de StreamsControladores de StreamConstrutores assíncronosAsync em métodos de classeRecapitulação — Buscador de dados15Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão RepositoryPratique por conta própria: Compilador de Dart online