Menu
Coddy logo textTech

Padrão Adapter

Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 95 de 110.

O padrão Adapter permite que interfaces incompatíveis trabalhem juntas. Ele atua como uma ponte entre duas classes que, de outra forma, não poderiam colaborar, envolvendo uma classe existente com uma nova interface que os clientes esperam. Isso é particularmente útil ao integrar bibliotecas de terceiros ou código legado em sua aplicação.

Imagine que você tem um sistema que trabalha com dados JSON, mas precisa integrar um componente que apenas gera XML. Em vez de modificar qualquer uma das classes, você cria um adaptador que traduz entre eles:

// Interface existente que seu sistema espera
abstract class JsonData {
  String getJson();
}

// Classe legada com interface incompatível
class XmlService {
  String getXml() {
    return '<data><name>John</name></data>';
  }
}

// O Adapter torna o XmlService compatível com JsonData
class XmlToJsonAdapter implements JsonData {
  final XmlService _xmlService;

  XmlToJsonAdapter(this._xmlService);

  @override
  String getJson() {
    String xml = _xmlService.getXml();
    // Converte XML para JSON (simplificado)
    return '{"name": "John"}';
  }
}

void main() {
  var xmlService = XmlService();
  var adapter = XmlToJsonAdapter(xmlService);

  print(adapter.getJson());  // {"name": "John"}
}

O XmlToJsonAdapter envolve o XmlService e implementa a interface JsonData. O código do cliente trabalha com a familiar interface JsonData sem saber que os dados vieram originalmente de uma fonte XML. O adaptador lida com toda a tradução internamente.

Use o padrão Adapter quando você quiser usar uma classe existente, mas sua interface não corresponde ao que você precisa, ou quando você precisa criar uma classe reutilizável que coopere com classes que possuem interfaces incompatíveis.

challenge icon

Desafio

Fácil

Vamos construir um sistema adaptador de reprodutor de mídia! Imagine que você tem um sistema de áudio moderno que espera que todas as fontes de mídia forneçam áudio por meio de uma interface padrão, mas você precisa integrar um antigo toca-discos de vinil que tem uma maneira completamente diferente de produzir som.

Você criará dois arquivos para organizar sua implementação do padrão adapter:

  • media_adapter.dart: Este arquivo contém sua interface e as classes do adaptador. Defina uma classe abstrata AudioSource que seu sistema moderno espera - ela deve ter um método String playAudio() que retorna a saída de áudio como uma string. Em seguida, crie uma classe VinylPlayer representando o equipamento legado - ela possui um campo trackName e um método String spinRecord() que retorna *crackle* Playing vinyl: [trackName] *crackle*. Por fim, construa um VinylAdapter que implementa AudioSource e envolve um VinylPlayer. O método playAudio() do adaptador deve chamar o método spinRecord() do toca-discos de vinil e retornar o resultado - fazendo a ponte entre as interfaces incompatíveis.
  • main.dart: Importe seu arquivo de adaptador de mídia e demonstre como o adaptador faz o antigo toca-discos de vinil funcionar com o código que espera a interface moderna AudioSource. Crie um VinylPlayer com o nome da faixa Bohemian Rhapsody. Em seguida, crie um VinylAdapter que envolva este toca-discos de vinil. Armazene o adaptador em uma variável do tipo AudioSource para provar que ele está em conformidade com a interface esperada. Chame playAudio() em sua fonte de áudio e imprima o resultado.

A beleza deste padrão é que seu código principal funciona com a interface padrão AudioSource - ele não precisa saber que, nos bastidores, um antigo toca-discos de vinil está fazendo o trabalho real!

Saída esperada:

*crackle* Playing vinyl: Bohemian Rhapsody *crackle*

Folha de consulta

O padrão Adapter permite que interfaces incompatíveis trabalhem juntas, agindo como uma ponte entre duas classes que, de outra forma, não poderiam colaborar.

O adapter envolve uma classe existente com uma nova interface que os clientes esperam, tornando-o útil ao integrar bibliotecas de terceiros ou código legado.

Estrutura Básica

// Interface alvo que os clientes esperam
abstract class JsonData {
  String getJson();
}

// Classe existente com interface incompatível
class XmlService {
  String getXml() {
    return '<data><name>John</name></data>';
  }
}

// O Adapter torna o XmlService compatível com JsonData
class XmlToJsonAdapter implements JsonData {
  final XmlService _xmlService;

  XmlToJsonAdapter(this._xmlService);

  @override
  String getJson() {
    String xml = _xmlService.getXml();
    // Converte XML para JSON
    return '{"name": "John"}';
  }
}

Uso

void main() {
  var xmlService = XmlService();
  var adapter = XmlToJsonAdapter(xmlService);

  print(adapter.getJson());  // {"name": "John"}
}

O adapter implementa a interface alvo enquanto envolve a classe incompatível. O código do cliente trabalha com a interface familiar sem conhecer a implementação subjacente.

Quando usar: Quando você precisa usar uma classe existente, mas sua interface não corresponde ao que você precisa, ou ao criar uma classe reutilizável que coopera com classes que possuem interfaces incompatíveis.

Experimente você mesmo

import 'media_adapter.dart';

void main() {
  // TODO: Crie um VinylPlayer com o nome da faixa "Bohemian Rhapsody"
  
  // TODO: Crie um VinylAdapter que envolva o vinyl player
  
  // TODO: Armazene o adaptador em uma variável do tipo AudioSource
  
  // TODO: Chame playAudio() em sua fonte de áudio e imprima o resultado
  
}
quiz iconTeste seus conhecimentos

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