Mobile App Development · Dart/Flutter

21 · Mixins e Extensions — Exercícios

Seis exercícios progressivos: de um mixin simples até múltiplos mixins, mixin com estado, on e extensions em String e double.

Exercício 1

exercicio_01_mixin_logavel.dart

Cria o mixin Logavel com um método log e aplica em ServicoUsuario usando with. Mostra a forma mais básica de compartilhar comportamento entre classes sem herança.

// Demonstração de Mixin básico em Dart.
// Mixin é um mecanismo de reutilização de código que permite adicionar
// comportamentos a uma classe sem usar herança direta.
// Declarado com 'mixin'; aplicado em uma classe com 'with'.

// Define o mixin 'Logavel' com o comportamento de registrar logs.
mixin Logavel {
  // Método que pode ser usado por qualquer classe que aplique este mixin.
  void log(String mensagem) {
    print('[LOG] $mensagem');
  }
}

// 'with Logavel' adiciona o comportamento de log à classe ServicoUsuario
// sem precisar criar uma classe base em comum.
class ServicoUsuario with Logavel {
  void buscarUsuario() {
    // Chama o método 'log' herdado do mixin Logavel.
    log('Buscando usuario');
  }
}

void main() {
  ServicoUsuario servico = ServicoUsuario();
  // Chama o método do serviço que internamente usa o mixin.
  servico.buscarUsuario();
}

Saída esperada

[LOG] Buscando usuario

Como executar

dart run 21-Mixins_Extensions/02_Exercicios/codigo_fonte/exercicio_01_mixin_logavel.dart

Exercício 2

exercicio_02_multiplos_mixins.dart

Cria dois mixins independentes, Logavel e Auditavel, e aplica ambos na mesma classe com with Logavel, Auditavel. Mostra que uma classe pode combinar vários comportamentos de mixins diferentes.

// Demonstração de múltiplos mixins em uma única classe.
// Uma classe pode aplicar vários mixins usando 'with Mixin1, Mixin2, ...'.
// Cada mixin adiciona seus comportamentos de forma independente.

// Mixin para registro de mensagens de log.
mixin Logavel {
  // Arrow function: forma compacta de definir método com única expressão.
  void log(String mensagem) => print('[LOG] $mensagem');
}

// Mixin para registro de ações de auditoria.
mixin Auditavel {
  void auditar(String acao) => print('[AUDITORIA] $acao');
}

// A classe PedidoService herda comportamentos dos dois mixins simultaneamente.
// 'with Logavel, Auditavel' mescla ambos os comportamentos.
class PedidoService with Logavel, Auditavel {
  void criarPedido() {
    // Usa o método do mixin Logavel.
    log('Criando pedido');
    // Usa o método do mixin Auditavel.
    auditar('Pedido criado');
  }
}

void main() {
  PedidoService service = PedidoService();
  // Chama criarPedido que internamente aciona os dois mixins.
  service.criarPedido();
}

Saída esperada

[LOG] Criando pedido
[AUDITORIA] Pedido criado

Como executar

dart run 21-Mixins_Extensions/02_Exercicios/codigo_fonte/exercicio_02_multiplos_mixins.dart

Exercício 3

exercicio_03_mixin_com_estado.dart

Cria o mixin ContadorCliques, que declara o atributo cliques além do método registrarClique. Mostra que mixins também podem carregar estado, não apenas comportamento — cada classe que os usa recebe sua própria cópia.

// Demonstração de mixin com estado (atributo) em Dart.
// Mixins não precisam ser apenas comportamentos sem estado —
// eles também podem declarar atributos que ficam disponíveis nas classes que os usam.

// Mixin que adiciona um contador de cliques às classes que o aplicam.
mixin ContadorCliques {
  // Atributo de estado que pertence ao mixin — cada instância tem o seu.
  int cliques = 0;

  // Método que incrementa o contador a cada chamada.
  void registrarClique() {
    cliques++;
  }
}

// A classe Botao ganha o atributo 'cliques' e o método 'registrarClique'
// diretamente do mixin, sem precisar declarar nada.
class Botao with ContadorCliques {}

void main() {
  Botao botao = Botao();
  // Chama registrarClique duas vezes — o contador interno aumenta.
  botao.registrarClique();
  botao.registrarClique();
  // Acessa o atributo 'cliques' que veio do mixin — resultado: 2.
  print('Cliques: ${botao.cliques}');
}

Saída esperada

Cliques: 2

Como executar

dart run 21-Mixins_Extensions/02_Exercicios/codigo_fonte/exercicio_03_mixin_com_estado.dart

Exercício 4

exercicio_04_mixin_on.dart

Cria a classe base Entidade e o mixin Identificavel, restrito com on Entidade. Isso garante que o mixin só possa ser usado por subclasses de Entidade, permitindo acessar com segurança o atributo id definido na base.

// Demonstração de 'mixin on' — restrição de uso de mixin.
// 'mixin X on Y' significa que X só pode ser aplicado a classes que estendem Y.
// Isso garante que o mixin acesse membros definidos na classe base.

// Classe base com o atributo 'id' que o mixin vai acessar.
class Entidade {
  final String id;

  Entidade(this.id);
}

// 'on Entidade' restringe: apenas classes que herdam de Entidade podem usar este mixin.
// Dentro do mixin, podemos acessar 'id' porque garantidamente a classe base o tem.
mixin Identificavel on Entidade {
  void exibirId() {
    // 'id' é acessível porque o mixin só pode ser usado por subclasses de Entidade.
    print('ID: $id');
  }
}

// Produto herda de Entidade (necessário para aplicar Identificavel) e usa o mixin.
class Produto extends Entidade with Identificavel {
  // 'super.id' passa o valor do parâmetro para o construtor da classe pai (Entidade).
  Produto(super.id);
}

void main() {
  Produto produto = Produto('prod-001');
  // Chama o método do mixin que acessa o atributo da classe base.
  produto.exibirId();
}

Saída esperada

ID: prod-001

Como executar

dart run 21-Mixins_Extensions/02_Exercicios/codigo_fonte/exercicio_04_mixin_on.dart

Exercício 5

exercicio_05_extension_string.dart

Cria a extension TextoFormatado sobre String, adicionando o método capitalizar, que transforma o primeiro caractere em maiúsculo. Mostra como estender um tipo nativo do Dart sem herança.

// Demonstração de Extension Method em Dart sobre o tipo String.
// Extensions permitem adicionar novos métodos a tipos existentes
// sem modificar o código original da classe e sem herança.

// 'extension NomeDaExtension on Tipo' adiciona métodos ao tipo especificado.
extension TextoFormatado on String {
  // Novo método 'capitalizar' adicionado ao tipo String.
  // Converte o primeiro caractere para maiúscula e mantém o resto intacto.
  String capitalizar() {
    // Verifica se a String está vazia — nesse caso, retorna ela mesma sem alteração.
    if (isEmpty) {
      return this;
    }

    // this[0] acessa o primeiro caractere.
    // toUpperCase() converte para maiúscula.
    // substring(1) retorna a String a partir do segundo caractere.
    return '${this[0].toUpperCase()}${substring(1)}';
  }
}

void main() {
  String nome = 'dart';
  // 'capitalizar()' agora está disponível em qualquer String!
  // Resultado esperado: 'Dart'.
  print(nome.capitalizar());
}

Saída esperada

Dart

Como executar

dart run 21-Mixins_Extensions/02_Exercicios/codigo_fonte/exercicio_05_extension_string.dart

Exercício 6

exercicio_06_extension_double.dart

Cria a extension Moeda sobre double, adicionando o método paraReal, que formata o valor como moeda brasileira com duas casas decimais. Mostra extensions aplicadas a tipos numéricos.

// Demonstração de Extension Method em Dart sobre o tipo double.
// Adiciona funcionalidades de formatação monetária diretamente ao tipo double.

// Estende o tipo double com método de formatação em reais (R$).
extension Moeda on double {
  // Converte o valor double para uma String formatada como moeda brasileira.
  // Arrow function: implementação compacta com uma única expressão de retorno.
  // toStringAsFixed(2) garante sempre 2 casas decimais (ex.: 49.90).
  String paraReal() => 'R\$ ${toStringAsFixed(2)}';
}

void main() {
  double preco = 49.9;
  // 'paraReal()' agora está disponível em qualquer double!
  // Resultado esperado: 'R$ 49.90'.
  print(preco.paraReal());
}

Saída esperada

R$ 49.90

Como executar

dart run 21-Mixins_Extensions/02_Exercicios/codigo_fonte/exercicio_06_extension_double.dart