Adapter — qué es, el patrón de transformación de interfaces para desarrollo móvil

Autor: IT Sectr Publicado: 2026-02-18 Tiempo de lectura: 10 min

Adapter es un patrón de diseño estructural que convierte la interfaz de una clase en otra interfaz esperada por el cliente. En el desarrollo móvil, Adapter se usa más frecuentemente en RecyclerView.Adapter para Android y UITableViewDataSource para iOS. Según Google I/O (2024), RecyclerView se utiliza en el 95% de las aplicaciones Android para mostrar listas, y cada una requiere su propia implementación de Adapter.

Puntos clave

  • Adapter es un patrón estructural que permite que objetos con interfaces incompatibles trabajen juntos
  • RecyclerView.Adapter es una implementación del patrón en Android, que convierte datos en ViewHolders para la lista
  • UITableViewDataSource es una implementación en iOS, que combina los patrones Adapter y DataSource
  • Object Adapter usa composición: el adaptador contiene una referencia al objeto adaptado
  • Class Adapter usa herencia múltiple — solo posible en C++, no disponible en Java/Swift/Kotlin

¿Qué es el patrón Adapter?

Adapter es un patrón estructural que resuelve el problema de incompatibilidad de interfaces. Envuelve un objeto (Adaptee) en una clase (Adapter) con la interfaz esperada por el cliente (Target). El cliente trabaja con Target sin saber de la existencia de Adaptee. Es una variante del patrón Wrapper.

Kotlin
// Clase existente con interfaz incompatible
class LegacyAuthApi {
    fun loginWithToken(token: String): Map {
        return mapOf("status" to "ok", "user_id" to 42)
    }
}

// Interfaz objetivo (lo que espera el cliente)
interface AuthService {
    suspend fun login(credentials: Credentials): Result
}

// Adapter — transforma LegacyAuthApi en AuthService
class LegacyAuthAdapter(
    private val legacyApi: LegacyAuthApi
) : AuthService {
    override suspend fun login(credentials: Credentials): Result {
        val token = "${credentials.login}:${credentials.password}"
            .encodeToByteArray().let { Base64.encodeToString(it) }
        val response = legacyApi.loginWithToken(token)
        return if (response["status"] == "ok") {
            Result.success(User(response["user_id"] as Int))
        } else {
            Result.failure(AuthException("Login failed"))
        }
    }
}

LegacyAuthAdapter convierte la llamada login(credentials) en loginWithToken(token) para la API antigua. El cliente (ViewModel) trabaja a través de la interfaz AuthService y no sabe si debajo tiene el último Firebase Auth o una API heredada de hace una década. Esto permite reemplazar implementaciones sin cambiar el código del cliente.

Adapter en Android: RecyclerView y ListAdapter

RecyclerView.Adapter es la implementación más común del patrón Adapter en Android. Convierte datos (una lista de objetos) en ViewHolders que RecyclerView muestra en pantalla. Con la llegada de ListAdapter (Android Architecture Components), el patrón obtuvo soporte integrado para cálculo de diferencias (diff) para animar cambios.

Kotlin
class UserAdapter(
    private val onItemClick: (User) -> Unit
) : ListAdapter(DiffCallback()) {

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): UserViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_user, parent, false)
        return UserViewHolder(view)
    }

    override fun onBindViewHolder(
        holder: UserViewHolder,
        position: Int
    ) {
        holder.bind(getItem(position), onItemClick)
    }

    class UserViewHolder(itemView: View) :
        RecyclerView.ViewHolder(itemView) {
        
        private val tvName = itemView.findViewById(R.id.tvName)
        
        fun bind(user: User, click: (User) -> Unit) {
            tvName.text = user.name
            itemView.setOnClickListener { click(user) }
        }
    }

    private class DiffCallback : DiffUtil.ItemCallback() {
        override fun areItemsTheSame(old: User, new: User) = old.id == new.id
        override fun areContentsTheSame(old: User, new: User) = old == new
    }
}

UserAdapter usa ListAdapter con DiffUtil para un redibujado eficiente de la lista. Cuando los datos se actualizan, DiffCallback calcula la diferencia entre la lista antigua y la nueva, y RecyclerView anima solo los elementos cambiados — sin redibujar toda la lista. Esta es una ventaja clave sobre el anterior ListView, que requería notifyDataSetChanged() manual.

Adapter en iOS: UITableView y DiffableDataSource

UITableViewDataSource es a la vez el patrón Adapter y el patrón DataSource. Convierte datos del modelo en celdas de tabla. Desde iOS 13, Apple presentó DiffableDataSource — un reemplazo moderno que, como ListAdapter en Android, calcula automáticamente los cambios y anima las actualizaciones.

Swift
class UserTableViewAdapter: NSObject, UITableViewDataSource {
    var items: [User] = []
    
    func tableView(_ tableView: UITableView,
                    numberOfRowsInSection section: Int) -> Int {
        items.count
    }
    
    func tableView(_ tableView: UITableView,
                    cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(
            withIdentifier: "UserCell", for: indexPath)
        let user = items[indexPath.row]
        cell.textLabel?.text = user.name
        return cell
    }
}

// Versión moderna: DiffableDataSource
enum Section { case main }

class ModernUserAdapter {
    var dataSource: UITableViewDiffableDataSource<Section, User>!
    
    func configure(for tableView: UITableView) {
        dataSource = UITableViewDiffableDataSource(
            tableView: tableView
        ) { tableView, indexPath, user in
            let cell = tableView.dequeueReusableCell(
                withIdentifier: "UserCell", for: indexPath)
            cell.textLabel?.text = user.name
            return cell
        }
    }
    
    func update(with users: [User]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, User>()
        snapshot.appendSections([.main])
        snapshot.appendItems(users)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

ModernUserAdapter con DiffableDataSource anima automáticamente los cambios al actualizar los datos: los elementos aparecen, desaparecen o se mueven con animaciones suaves. DiffableDataSource de iOS y ListAdapter de Android resuelven el mismo problema con diferentes API pero la misma lógica — cálculo de diferencias + animación automática.

Object Adapter vs Class Adapter

Object Adapter usa composición: el adaptador contiene una referencia al objeto Adaptee y le delega las llamadas. Class Adapter usa herencia: el adaptador hereda de Adaptee e implementa la interfaz objetivo simultáneamente. En Java, Swift y Kotlin, Class Adapter es imposible por la falta de herencia múltiple — solo queda Object Adapter.

CaracterísticaObject AdapterClass Adapter
MecanismoComposición (contiene Adaptee)Herencia (extiende Adaptee)
FlexibilidadMayor — Adaptee se puede cambiar en tiempo de ejecuciónMenor — Adaptee se fija en tiempo de compilación
AcoplamientoDébil (a través de interfaz)Fuerte (a través de herencia)
Adaptación de subclasesSí — Adaptee puede ser cualquier subclaseNo — solo una superclase específica
DisponibilidadJava, Kotlin, Swift, C++, C#Solo lenguajes con herencia múltiple (C++)

En el desarrollo móvil, solo se usa Object Adapter. RecyclerView.Adapter contiene referencias a datos y LayoutInflater, UITableViewDataSource contiene un array de elementos. La composición hace que el código sea más flexible y comprobable en comparación con la herencia.

Ejemplos de Adapter en proyectos reales

Adapter no solo se usa para listas. Consideremos tres escenarios de la práctica de desarrollo móvil donde el patrón resuelve el problema de integrar componentes incompatibles.

EscenarioAdapteeTargetAdapter
Integración de una librería auth antiguaLegacyAuthLib (basado en callbacks)AuthService (suspend)LegacyAuthAdapter
Adaptación de JSON a un nuevo modeloGsonParserJsonParser (Kotlinx Serialization)GsonAdapter
Interfaz unificada para servicios pushFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

Cada Adapter encapsula la lógica de conversión: GsonAdapter traduce una cadena JSON a un objeto mediante @SerializedName, PushServiceAdapter abstrae el registro de tokens para diferentes plataformas. El cliente (lógica de negocio) trabaja con una interfaz unificada y no depende de la implementación específica.

Errores típicos al implementar Adapter

Los errores en la implementación de Adapter provocan lentitud en las listas, fugas de memoria y fallos en la actualización de datos. Veamos tres problemas comunes.

Operaciones pesadas en onBindViewHolder

onBindViewHolder se llama para cada celda durante el desplazamiento. Crear objetos, analizar JSON o llamar a la base de datos en este método provoca caídas de fotogramas (frame drops). Solución: realizar todos los cálculos pesados de antemano, pasando datos ya preparados al adaptador. Usar librerías de caché para imágenes — Glide, Coil, Kingfisher.

Ignorar DiffUtil / DiffableDataSource

Llamar a notifyDataSetChanged() en cada actualización redibuja toda la lista, causando parpadeo y pérdida de foco en las entradas. Solución: usar ListAdapter con DiffUtil en Android o DiffableDataSource en iOS. El cálculo de diferencias toma <1 ms para listas de hasta 1000 elementos y garantiza animaciones suaves.

Almacenar objetos pesados en el adaptador

El adaptador vive tanto como RecyclerView/UITableView. Almacenar Bitmaps o arrays grandes en el adaptador provoca fugas de memoria en cambios de configuración (rotación de pantalla, cambio de tema). Solución: el adaptador debe almacenar solo modelos de datos ligeros (data class / struct), y cargar recursos pesados a través de ViewHolder bajo demanda.

Preguntas frecuentes

¿Cuál es la diferencia entre Adapter y Proxy?

Adapter convierte la interfaz de Adaptee a la interfaz de Target — el cliente obtiene una nueva forma de interacción. Proxy proporciona la misma interfaz que el objeto original, pero añade control de acceso, caché o inicialización diferida. Proxy no cambia la interfaz, Adapter sí.

¿Por qué RecyclerView.Adapter es un ejemplo del patrón Adapter?

RecyclerView.Adapter convierte datos (por ejemplo, una lista de User) en un ViewHolder que RecyclerView puede mostrar. RecyclerView espera un ViewHolder, los datos están en formato List<User> — el Adapter adapta uno al otro. Adicionalmente, el Adapter gestiona el ciclo de vida de los ViewHolders a través del pool de reciclaje, mejorando el rendimiento de la lista.

¿Cuál es la diferencia entre Object Adapter y Class Adapter?

Object Adapter usa composición: contiene una referencia a Adaptee y le delega las llamadas. Class Adapter usa herencia múltiple: hereda de Adaptee e implementa Target. En Java, Kotlin y Swift, solo está disponible Object Adapter debido a la falta de herencia múltiple de clases. Class Adapter solo es posible en C++.

¿Cuándo usar Adapter en lugar de Facade?

Adapter se usa cuando se necesita hacer que dos clases existentes funcionen juntas convirtiendo la interfaz de una de ellas. Facade se usa cuando se necesita simplificar la interacción con un subsistema complejo proporcionando una interfaz simple. Adapter es para compatibilidad, Facade es para simplificación.

¿Se puede implementar Adapter sin interfaz?

Sí, mediante cierres. En lugar de una interfaz con un método, se puede pasar una función. Por ejemplo, en Swift: let adapter = { (data: Data) -> [CellConfig] in /* conversión */ }. Este enfoque se llama adaptador funcional. Sin embargo, para 2+ métodos, un protocolo/interfaz sigue siendo preferible — hace explícito el contrato.

Resumen

  • Adapter es un patrón estructural que convierte una interfaz Adaptee incompatible en un Target mediante una clase intermediaria
  • RecyclerView.Adapter (Android) y UITableViewDataSource (iOS) son los principales ejemplos del patrón en desarrollo móvil
  • Object Adapter usa composición; Class Adapter (solo C++) usa herencia múltiple
  • ListAdapter + DiffUtil (Android) y DiffableDataSource (iOS) son evoluciones modernas con diff automático y animación
  • Operaciones pesadas en onBindViewHolder, ignorar DiffUtil y almacenar recursos en el adaptador son errores comunes
  • Adapter se usa para integrar código heredado, adaptar formatos y unificar diferentes implementaciones
  • Recomendación: usa ListAdapter/DiffableDataSource en lugar de la gestión manual de actualizaciones de lista

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también