Patrón Adapter
Una clase que envuelve a otra cuya interfaz no es la que espera tu código y traduce cada llamada: usar una biblioteca de terceros, un sistema antiguo o un sensor de otro fabricante sin tocarlos.
nivel básicoTambién: adaptador, wrapper, envoltorio
Visualízalo paso a paso
Cambia los datos, dale a reproducir y sigue cada paso en el dibujo, en la línea de Java que se ejecuta y en sus variables.
Adapter
Escribe los importes que la tienda cobra con la pasarela externa, separados por espacios.
Paso 1
La tienda solo conoce la interfaz Pago. La pasarela de terceros cobra en céntimos y responde con un texto: el adaptador hace que encajen.
1class PasarelaAdapter implements Pago {
2 private final PasarelaExterna pasarela;
3
4 public boolean cobrar(double euros) { // pagos = 3
5 long centimos = Math.round(euros * 100);
6 String codigo = pasarela.realizarCargo(centimos, "EUR");
7 return codigo.equals("OK");
8 }
9}Variables
- pagos
- 3
Atajos con el foco dentro del visualizador: ← → paso a paso, Espacio reproducir o pausar, Inicio/Fin ir al principio o al final.
La idea
Tu programa trabaja con una interfaz (Pago, con cobrar(euros)), y aparece una clase que hace lo que necesitas pero con otra forma: una pasarela que cobra en céntimos con realizarCargo(centimos, moneda) y responde con un código de texto. No puedes cambiar la pasarela (es de otra empresa) y no quieres llenar tu código de casos especiales.
El patrón Adapter crea una clase intermedia, el adaptador, que implementa la interfaz que espera tu programa y guarda dentro el objeto incompatible. Cada método traduce la llamada: convierte los euros en céntimos, llama a la pasarela y convierte su respuesta en el boolean que esperas. Para el resto del programa, la pasarela es un Pago más.
Es el patrón de los enchufes de viaje: ni el cargador ni la pared cambian. Lo usas constantemente sin llamarlo así: Arrays.asList adapta un array a una List, InputStreamReader adapta bytes a caracteres y el Adapter de un RecyclerView en Android adapta tus datos a las filas de una lista.
Cuándo usarlo
- Quieres usar una clase existente (de una biblioteca, de un sistema antiguo, de otro equipo) cuya interfaz no coincide con la que usa tu código.
- Hay que integrar varias implementaciones de terceros detrás de una misma interfaz (varias pasarelas de pago, varios sensores, varios servicios de correo).
- Quieres aislar tu código de una API externa que podría cambiar: si cambia, solo se toca el adaptador.
Cuándo no
- Si puedes cambiar la clase para que implemente tu interfaz directamente: es más sencillo.
- Si las dos interfaces son tan distintas que el adaptador tiene que inventarse comportamiento: el problema es de diseño, no de forma.
Participantes
- Objetivo. La interfaz que espera tu código (
Pago). - Adaptado. La clase existente con la interfaz incompatible (
PasarelaExterna). - Adaptador. Implementa el objetivo, guarda el adaptado y traduce cada llamada y cada resultado (
PasarelaAdapter). - Cliente. Trabaja solo con el objetivo (
Tienda) y no sabe que hay un adaptador.
Diagrama de clases
Ver el diagrama en PlantUML
1@startuml
2interface Pago {
3 +cobrar(euros : double) : boolean
4}
5class TarjetaPropia {
6 +cobrar(euros : double) : boolean
7}
8class PasarelaExterna {
9 +realizarCargo(centimos : long, moneda : String) : String
10}
11class PasarelaAdapter {
12 -pasarela : PasarelaExterna
13 +cobrar(euros : double) : boolean
14}
15class Tienda {
16 +finalizarCompra(pago : Pago, total : double) : void
17}
18Pago <|.. TarjetaPropia
19Pago <|.. PasarelaAdapter
20PasarelaAdapter --> "1" PasarelaExterna : pasarela
21Tienda ..> Pago : usa
22@endumlPuedes copiarlo en el editor de diagramas UML y modificarlo.
El código
Una pasarela de pago de terceros
La tienda no cambia: el adaptador convierte euros en céntimos y el código de la pasarela en true o false.
1import java.util.*;
2
3/** La interfaz que usa nuestro programa. */
4interface Pago {
5 boolean cobrar(double euros);
6}
7
8class TarjetaPropia implements Pago {
9 public boolean cobrar(double euros) {
10 System.out.println("Tarjeta: cobrados " + String.format(Locale.ROOT, "%.2f", euros).replace('.', ',') + " €");
11 return true;
12 }
13}
14
15/** Una biblioteca de terceros: no se puede cambiar y su interfaz no es la nuestra. */
16class PasarelaExterna {
17 String realizarCargo(long centimos, String moneda) {
18 if (centimos > 50_000) return "KO-LIMITE";
19 System.out.println("Pasarela: cargo de " + centimos + " céntimos en " + moneda);
20 return "OK";
21 }
22}
23
24/** El adaptador: tiene la interfaz que espera nuestro código y traduce cada llamada a la pasarela. */
25class PasarelaAdapter implements Pago {
26 private final PasarelaExterna pasarela;
27
28 PasarelaAdapter(PasarelaExterna pasarela) { this.pasarela = pasarela; }
29
30 public boolean cobrar(double euros) {
31 long centimos = Math.round(euros * 100); // euros → céntimos
32 return pasarela.realizarCargo(centimos, "EUR").equals("OK"); // "OK" / "KO-…" → true / false
33 }
34}
35
36/** El cliente: solo conoce Pago, así que acepta la pasarela sin cambiar una línea. */
37class Tienda {
38 void finalizarCompra(Pago pago, double total) {
39 System.out.println(pago.cobrar(total) ? "Compra pagada" : "Pago rechazado");
40 }
41}
42
43public class Main {
44 public static void main(String[] args) {
45 Tienda tienda = new Tienda();
46 tienda.finalizarCompra(new TarjetaPropia(), 12.5);
47 Pago pasarela = new PasarelaAdapter(new PasarelaExterna());
48 tienda.finalizarCompra(pasarela, 89.99);
49 tienda.finalizarCompra(pasarela, 600);
50 }
51}from abc import ABC, abstractmethod
class Pago(ABC):
"""La interfaz que usa nuestro programa."""
@abstractmethod
def cobrar(self, euros): ...
class TarjetaPropia(Pago):
def cobrar(self, euros):
print(f"Tarjeta: cobrados {euros:.2f} €".replace(".", ","))
return True
class PasarelaExterna:
"""Una biblioteca de terceros: no se puede cambiar y su interfaz no es la nuestra."""
def realizar_cargo(self, centimos, moneda):
if centimos > 50_000:
return "KO-LIMITE"
print(f"Pasarela: cargo de {centimos} céntimos en {moneda}")
return "OK"
class PasarelaAdapter(Pago):
"""El adaptador: tiene la interfaz que espera nuestro código y traduce cada llamada a la pasarela."""
def __init__(self, pasarela):
self._pasarela = pasarela
def cobrar(self, euros):
centimos = round(euros * 100) # euros → céntimos
return self._pasarela.realizar_cargo(centimos, "EUR") == "OK" # "OK" / "KO-…" → True / False
class Tienda:
"""El cliente: solo conoce Pago, así que acepta la pasarela sin cambiar una línea."""
def finalizar_compra(self, pago, total):
print("Compra pagada" if pago.cobrar(total) else "Pago rechazado")
tienda = Tienda()
tienda.finalizar_compra(TarjetaPropia(), 12.5)
pasarela = PasarelaAdapter(PasarelaExterna())
tienda.finalizar_compra(pasarela, 89.99)
tienda.finalizar_compra(pasarela, 600)// La «interfaz» que usa nuestro programa: cualquier objeto con cobrar(euros)
class TarjetaPropia {
cobrar(euros) {
console.log(`Tarjeta: cobrados ${euros.toFixed(2).replace(".", ",")} €`);
return true;
}
}
/** Una biblioteca de terceros: no se puede cambiar y su interfaz no es la nuestra. */
class PasarelaExterna {
realizarCargo(centimos, moneda) {
if (centimos > 50_000) return "KO-LIMITE";
console.log(`Pasarela: cargo de ${centimos} céntimos en ${moneda}`);
return "OK";
}
}
/** El adaptador: tiene la interfaz que espera nuestro código y traduce cada llamada a la pasarela. */
class PasarelaAdapter {
constructor(pasarela) { this.pasarela = pasarela; }
cobrar(euros) {
const centimos = Math.round(euros * 100); // euros → céntimos
return this.pasarela.realizarCargo(centimos, "EUR") === "OK"; // "OK" / "KO-…" → true / false
}
}
/** El cliente: solo usa cobrar, así que acepta la pasarela sin cambiar una línea. */
class Tienda {
finalizarCompra(pago, total) {
console.log(pago.cobrar(total) ? "Compra pagada" : "Pago rechazado");
}
}
const tienda = new Tienda();
tienda.finalizarCompra(new TarjetaPropia(), 12.5);
const pasarela = new PasarelaAdapter(new PasarelaExterna());
tienda.finalizarCompra(pasarela, 89.99);
tienda.finalizarCompra(pasarela, 600);using System;
using System.Globalization;
// La interfaz que usa nuestro programa.
interface IPago {
bool Cobrar(decimal euros);
}
class TarjetaPropia : IPago {
public bool Cobrar(decimal euros) {
Console.WriteLine(quot;Tarjeta: cobrados {euros.ToString("F2", CultureInfo.InvariantCulture).Replace('.', ',')} €");
return true;
}
}
// Una biblioteca de terceros: no se puede cambiar y su interfaz no es la nuestra.
class PasarelaExterna {
public string RealizarCargo(long centimos, string moneda) {
if (centimos > 50_000) return "KO-LIMITE";
Console.WriteLine(quot;Pasarela: cargo de {centimos} céntimos en {moneda}");
return "OK";
}
}
// El adaptador: tiene la interfaz que espera nuestro código y traduce cada llamada a la pasarela.
class PasarelaAdapter : IPago {
private readonly PasarelaExterna pasarela;
public PasarelaAdapter(PasarelaExterna pasarela) => this.pasarela = pasarela;
public bool Cobrar(decimal euros) {
long centimos = (long)Math.Round(euros * 100); // euros → céntimos
return pasarela.RealizarCargo(centimos, "EUR") == "OK"; // "OK" / "KO-…" → true / false
}
}
// El cliente: solo conoce IPago, así que acepta la pasarela sin cambiar una línea.
class Tienda {
public void FinalizarCompra(IPago pago, decimal total) {
Console.WriteLine(pago.Cobrar(total) ? "Compra pagada" : "Pago rechazado");
}
}
class Program {
static void Main() {
var tienda = new Tienda();
tienda.FinalizarCompra(new TarjetaPropia(), 12.5m);
IPago pasarela = new PasarelaAdapter(new PasarelaExterna());
tienda.FinalizarCompra(pasarela, 89.99m);
tienda.FinalizarCompra(pasarela, 600m);
}
}<?php
/** La interfaz que usa nuestro programa. */
interface Pago {
public function cobrar(float $euros): bool;
}
class TarjetaPropia implements Pago {
public function cobrar(float $euros): bool {
echo "Tarjeta: cobrados ", number_format($euros, 2, ",", ""), " €\n";
return true;
}
}
/** Una biblioteca de terceros: no se puede cambiar y su interfaz no es la nuestra. */
class PasarelaExterna {
public function realizarCargo(int $centimos, string $moneda): string {
if ($centimos > 50000) return "KO-LIMITE";
echo "Pasarela: cargo de $centimos céntimos en $moneda\n";
return "OK";
}
}
/** El adaptador: tiene la interfaz que espera nuestro código y traduce cada llamada a la pasarela. */
class PasarelaAdapter implements Pago {
public function __construct(private PasarelaExterna $pasarela) {}
public function cobrar(float $euros): bool {
$centimos = (int) round($euros * 100); // euros → céntimos
return $this->pasarela->realizarCargo($centimos, "EUR") === "OK"; // "OK" / "KO-…" → true / false
}
}
/** El cliente: solo conoce Pago, así que acepta la pasarela sin cambiar una línea. */
class Tienda {
public function finalizarCompra(Pago $pago, float $total): void {
echo $pago->cobrar($total) ? "Compra pagada" : "Pago rechazado", "\n";
}
}
$tienda = new Tienda();
$tienda->finalizarCompra(new TarjetaPropia(), 12.5);
$pasarela = new PasarelaAdapter(new PasarelaExterna());
$tienda->finalizarCompra($pasarela, 89.99);
$tienda->finalizarCompra($pasarela, 600);Salida al ejecutarlo (la misma en los 5 lenguajes)
Tarjeta: cobrados 12,50 € Compra pagada Pasarela: cargo de 8999 céntimos en EUR Compra pagada Pago rechazado
Adaptadores de la biblioteca
Algunos adaptadores que ya usas en Java y en Android.
1// Arrays.asList adapta un array a la interfaz List (sin copiarlo)
2String[] ciclos = {"DAM", "DAW", "ASIR"};
3List<String> lista = Arrays.asList(ciclos);
4
5// InputStreamReader adapta un flujo de bytes (InputStream) a uno de caracteres (Reader)
6BufferedReader teclado = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
7
8// Un método que espera un Runnable acepta cualquier método con la misma forma: la referencia
9// a método hace de adaptador sin escribir la clase
10Thread hilo = new Thread(this::descargarFichero);
11
12// Android: RecyclerView.Adapter adapta una lista de datos a las vistas de una lista en pantalla
13class AlumnoAdapter extends RecyclerView.Adapter<AlumnoAdapter.Fila> { /* ... */ }import io
# TextIOWrapper adapta un flujo de bytes a uno de texto (lo que hace open() por dentro)
binario = open("notas.csv", "rb")
texto = io.TextIOWrapper(binario, encoding="utf-8")
# En Python basta con tener los métodos que se esperan («duck typing»): un adaptador es
# cualquier clase que los ofrezca y llame por dentro a la que no encaja.
class LectorDeLista:
def __init__(self, lineas):
self._lineas = iter(lineas)
def readline(self): # la interfaz que espera quien lee ficheros
return next(self._lineas, "")// util.promisify adapta una función con callback a una que devuelve una promesa
import { promisify } from "node:util";
import { readFile } from "node:fs";
const leer = promisify(readFile);
const contenido = await leer("notas.csv", "utf8");
// Array.from adapta cualquier cosa iterable o con length (un NodeList, un Set) a un array
const botones = Array.from(document.querySelectorAll("button"));// StreamReader adapta un Stream (bytes) a un TextReader (caracteres)
using var lector = new StreamReader(File.OpenRead("notas.csv"), Encoding.UTF8);
// AsReadOnly adapta una List<T> a la interfaz IReadOnlyList<T> sin copiarla
IReadOnlyList<string> ciclos = new List<string> { "DAM", "DAW" }.AsReadOnly();
// En Android con Xamarin/MAUI, los Adapter de las listas convierten datos en filas de la pantalla<?php
// ArrayIterator adapta un array a la interfaz Iterator de la SPL
$it = new ArrayIterator(["DAM", "DAW", "ASIR"]);
foreach ($it as $ciclo) echo $ciclo, "\n";
// Los frameworks definen interfaces (PSR) y cada biblioteca trae su adaptador: Flysystem, por
// ejemplo, usa la misma interfaz para guardar ficheros en disco, en S3 o por FTP.
$disco = new League\Flysystem\Filesystem(new League\Flysystem\Local\LocalFilesystemAdapter("/datos"));En la práctica
Arrays.asList,InputStreamReader,OutputStreamWriteryCollections.enumerationen Java.- El
AdapterdeRecyclerViewy deListViewen Android: convierte cada dato en una fila de la lista. - Las integraciones con pasarelas de pago, servicios de correo o almacenamiento en la nube: una interfaz propia y un adaptador por proveedor.
- Los conversores de JSON a objetos y las capas que envuelven una API REST antigua para que parezca una clase normal.
Errores típicos
- Meter lógica de negocio en el adaptador: solo debe traducir. Si decide cosas, ya no es un adaptador.
- Que el cliente pregunte si el objeto es un adaptador (
instanceof PasarelaAdapter): se pierde todo lo ganado. - Olvidar traducir también los errores y los valores especiales (códigos de error,
null, unidades distintas). - Heredar de la clase adaptada en vez de envolverla: ata el adaptador a una implementación concreta y expone métodos que no quieres.
Ejercicios
Cada ejercicio se corrige solo con sus pruebas (algunas ocultas). Escribe tu solución en el editor y pulsa Ejecutar o Comprobar; la solución explicada está debajo, por si te atascas.
1. Sensores de otros fabricantes
El sistema de la casa trabaja con la interfaz Termometro (nombre y grados Celsius). Han comprado sensores de dos fabricantes que no la cumplen: uno mide en Fahrenheit y otro en décimas de kelvin y solo tiene número de serie. Escribe FahrenheitAdapter y KelvinAdapter para que el sistema los use sin cambiar nada más.
- Cada línea:
celsius nombre grados,fahrenheit id gradosokelvin serie decimas. Salida:nombre: X,X °Cpor sensor yMedia: M °C · más caliente: nombre. - Fahrenheit: °C = (°F − 32) × 5 / 9, y el nombre es su
getId(). Kelvin: °C = décimas / 10 − 273,15, y el nombre esindustrial-seguido del número de serie.
Ejemplo
celsius cocina 21.5 fahrenheit garaje 50 kelvin 4471 2981
cocina: 21,5 °C garaje: 10,0 °C industrial-4471: 25,0 °C Media: 18,8 °C · más caliente: industrial-4471
Ver la solución explicada
1import java.util.*;
2
3/** La interfaz que usa el sistema de la casa. */
4interface Termometro {
5 String nombre();
6 double celsius();
7}
8
9class SensorCelsius implements Termometro {
10 private final String nombre;
11 private final double grados;
12 SensorCelsius(String nombre, double grados) { this.nombre = nombre; this.grados = grados; }
13 public String nombre() { return nombre; }
14 public double celsius() { return grados; }
15}
16
17// ───── Clases de otros fabricantes: no se pueden modificar ─────
18
19/** Mide en grados Fahrenheit. */
20class SensorAmericano {
21 private final String id;
22 private final double fahrenheit;
23 SensorAmericano(String id, double fahrenheit) { this.id = id; this.fahrenheit = fahrenheit; }
24 String getId() { return id; }
25 double leerFahrenheit() { return fahrenheit; }
26}
27
28/** Mide en décimas de kelvin, como número entero, y no tiene nombre: solo un número de serie. */
29class SensorIndustrial {
30 private final int serie;
31 private final int decimasKelvin;
32 SensorIndustrial(int serie, int decimasKelvin) { this.serie = serie; this.decimasKelvin = decimasKelvin; }
33 int numeroDeSerie() { return serie; }
34 int lecturaDecimasKelvin() { return decimasKelvin; }
35}
36
37class FahrenheitAdapter implements Termometro {
38 private final SensorAmericano sensor;
39 FahrenheitAdapter(SensorAmericano sensor) { this.sensor = sensor; }
40 public String nombre() { return sensor.getId(); }
41 public double celsius() { return (sensor.leerFahrenheit() - 32) * 5 / 9; }
42}
43
44class KelvinAdapter implements Termometro {
45 private final SensorIndustrial sensor;
46 KelvinAdapter(SensorIndustrial sensor) { this.sensor = sensor; }
47 public String nombre() { return "industrial-" + sensor.numeroDeSerie(); }
48 public double celsius() { return sensor.lecturaDecimasKelvin() / 10.0 - 273.15; }
49}
50
51public class Main {
52 static String grados(double c) {
53 return String.format(Locale.ROOT, "%.1f", c).replace('.', ',') + " °C";
54 }
55
56 public static void main(String[] args) {
57 Scanner sc = new Scanner(System.in);
58 List<Termometro> termometros = new ArrayList<>();
59 while (sc.hasNextLine()) {
60 String[] p = sc.nextLine().trim().split("\\s+");
61 if (p[0].isEmpty()) continue;
62 try {
63 switch (p[0]) {
64 case "celsius" -> termometros.add(new SensorCelsius(p[1], Double.parseDouble(p[2])));
65 case "fahrenheit" -> termometros.add(new FahrenheitAdapter(new SensorAmericano(p[1], Double.parseDouble(p[2]))));
66 case "kelvin" -> termometros.add(new KelvinAdapter(new SensorIndustrial(Integer.parseInt(p[1]), Integer.parseInt(p[2]))));
67 default -> System.out.println("Sensor desconocido: " + p[0]);
68 }
69 } catch (RuntimeException e) {
70 System.out.println("Línea no válida: " + String.join(" ", p));
71 }
72 }
73 if (termometros.isEmpty()) {
74 System.out.println("Sin sensores");
75 return;
76 }
77 double suma = 0;
78 Termometro max = termometros.get(0);
79 for (Termometro t : termometros) {
80 System.out.println(t.nombre() + ": " + grados(t.celsius()));
81 suma += t.celsius();
82 if (t.celsius() > max.celsius()) max = t;
83 }
84 System.out.println("Media: " + grados(suma / termometros.size()) + " · más caliente: " + max.nombre());
85 }
86}El main trata igual a los tres tipos de sensor: la diferencia de unidades y de nombres queda dentro de los adaptadores.
Si mañana llega un sensor de un tercer fabricante, se añade un adaptador y nada más cambia.
2. La libreta del programa antiguo
La aplicación nueva usa la interfaz Agenda, pero los contactos tienen que seguir guardándose en la LibretaAntigua, que guarda textos apellidos, nombre|telefono y tiene sitio limitado. Escribe LibretaAdapter, que traduce en los dos sentidos: de Contacto a texto al añadir y de texto a Contacto al buscar.
- Órdenes:
anadir nombre apellidos… teléfono,buscar texto(en el nombre o los apellidos, sin distinguir mayúsculas, en el orden en que se añadieron) ytotal. - El teléfono va al final, sin espacios. Antes de guardarlo se le quitan los guiones y debe tener 9 cifras; si no,
IllegalArgumentExceptionconTeléfono no válido: original. Si la libreta está llena (insertardevuelvefalse),IllegalStateExceptionconLa libreta está llena. buscarescribeApellidos, Nombre: teléfono(el teléfono ya limpio) oSin resultados.
Ejemplo
anadir Ana López 600-111-222 anadir Luis Pérez Gil 611-222-333 anadir Marta Lopez Ruiz 622333444 buscar lóp buscar LOPEZ buscar luis total
Añadido: Ana López Añadido: Luis Pérez Gil Añadido: Marta Lopez Ruiz López, Ana: 600111222 Lopez Ruiz, Marta: 622333444 Pérez Gil, Luis: 611222333 Contactos: 3
Ver la solución explicada
1import java.util.*;
2
3record Contacto(String nombre, String apellidos, String telefono) {}
4
5/** La interfaz que usa la aplicación nueva. */
6interface Agenda {
7 void anadir(Contacto c);
8 List<Contacto> buscar(String texto);
9 int total();
10}
11
12/**
13 * La libreta del programa antiguo: guarda cada contacto como un texto «apellidos, nombre|telefono»,
14 * cabe un número limitado y no se puede modificar.
15 */
16class LibretaAntigua {
17 private final String[] registros;
18 private int n;
19
20 LibretaAntigua(int capacidad) { registros = new String[capacidad]; }
21
22 /** false si está llena. */
23 boolean insertar(String registro) {
24 if (n == registros.length) return false;
25 registros[n++] = registro;
26 return true;
27 }
28
29 String[] todos() { return Arrays.copyOf(registros, n); }
30}
31
32/** Adapta la libreta antigua a la interfaz Agenda: traduce los contactos a texto y al revés. */
33class LibretaAdapter implements Agenda {
34 private final LibretaAntigua libreta;
35
36 LibretaAdapter(LibretaAntigua libreta) { this.libreta = libreta; }
37
38 public void anadir(Contacto c) {
39 String telefono = c.telefono().replaceAll("[\\s-]", "");
40 if (!telefono.matches("\\d{9}")) throw new IllegalArgumentException("Teléfono no válido: " + c.telefono());
41 if (!libreta.insertar(c.apellidos() + ", " + c.nombre() + "|" + telefono)) throw new IllegalStateException("La libreta está llena");
42 }
43
44 private static Contacto leer(String registro) {
45 String[] partes = registro.split("\\|");
46 String[] nombre = partes[0].split(", ", 2);
47 return new Contacto(nombre[1], nombre[0], partes[1]);
48 }
49
50 public List<Contacto> buscar(String texto) {
51 String t = texto.toLowerCase();
52 List<Contacto> encontrados = new ArrayList<>();
53 for (String r : libreta.todos()) {
54 Contacto c = leer(r);
55 if (c.nombre().toLowerCase().contains(t) || c.apellidos().toLowerCase().contains(t)) encontrados.add(c);
56 }
57 return encontrados;
58 }
59
60 public int total() { return libreta.todos().length; }
61}
62
63public class Main {
64 public static void main(String[] args) {
65 Agenda agenda = new LibretaAdapter(new LibretaAntigua(4));
66 Scanner sc = new Scanner(System.in);
67 while (sc.hasNextLine()) {
68 String linea = sc.nextLine().trim();
69 if (linea.isEmpty()) continue;
70 String[] p = linea.split("\\s+");
71 try {
72 switch (p[0]) {
73 case "anadir" -> {
74 if (p.length < 4) throw new IllegalArgumentException("Faltan datos: " + linea);
75 String apellidos = String.join(" ", Arrays.copyOfRange(p, 2, p.length - 1));
76 agenda.anadir(new Contacto(p[1], apellidos, p[p.length - 1]));
77 System.out.println("Añadido: " + p[1] + " " + apellidos);
78 }
79 case "buscar" -> {
80 List<Contacto> r = agenda.buscar(p.length > 1 ? p[1] : "");
81 if (r.isEmpty()) System.out.println("Sin resultados");
82 for (Contacto c : r) System.out.println(c.apellidos() + ", " + c.nombre() + ": " + c.telefono());
83 }
84 case "total" -> System.out.println("Contactos: " + agenda.total());
85 default -> System.out.println("Orden no válida: " + linea);
86 }
87 } catch (IllegalArgumentException | IllegalStateException e) {
88 System.out.println(e.getMessage());
89 }
90 }
91 }
92}La aplicación nueva no sabe que los datos se guardan como texto: si mañana se cambia la libreta por una base de datos, solo cambia el adaptador.
Un adaptador traduce datos, formatos y errores, pero no decide nada del negocio.
Test
Test: Adapter
0/5 respondidas · 0 aciertosElige una respuesta en cada pregunta: verás al momento si es correcta y por qué. Con un 80 % de aciertos se da por superada.
1.¿Qué hace un adaptador?
2.¿Por qué se usa un adaptador en vez de modificar la clase adaptada?
3.¿Cuál de estos es un adaptador de la biblioteca de Java?
4.La libreta antigua devuelve false cuando está llena y la aplicación espera una excepción. ¿Quién hace la conversión?
5.¿Qué no debería hacer un adaptador?