que-es-duck-typing

¿Qué es el Duck Typing?

  • 6 min

El Duck Typing es un concepto de tipificación en el cual el tipo o la clase de un objeto es determinado por sus métodos y propiedades en vez de su herencia explícita o su implementación de una interfaz.

El término duck typing proviene de la frase:

Si parece un pato, nada como un pato y hace quack como un pato, entonces probablemente sea un pato

Es decir, si un objeto tiene los métodos y propiedades que necesitamos, podemos tratarlo como si fuera del tipo que esperamos, sin importar su clase real.

curso-poo-duck-typing

Osea, cuak

En programación, esto se traduce en que el Duck Typing, el tipo de un objeto no está definido por su clase, sino por su comportamiento.

Es un concepto predominante en lenguajes dinámicos como Python o JavaScript, y representa una forma de ver los tipos de datos muy distinta a la de lenguajes estrictos como C++ o Java.

La filosofía: Comportamiento vs Identidad

Para entenderlo, imaginemos que estamos programando un sistema y necesitamos procesar algo. Por ejemplo, me viene al pelo, un función que recibe un pato 🦆.

  • En un Tipado Estático (C++, C# clásico), somos como un control de aduanas. Pedimos el pasaporte:

¿Es usted un Pato? Enséñeme su acreditación de tipo Pato o un carnet de IPato. Si no, no pasa

  • En el Duck Typing (Python, JS), se es más pragmáticos. No pedimos identificación. Simplemente intentamos interactuar:

¿Puedes graznar? Si puedes, pasa. Realmente me da igual si eres un pato, una persona disfrazada o una grabadora

En resumen, en el Duck Typing nos importa qué puede hacer el objeto, no qué es el objeto.

Decidir si el Duck Typing es un concepto de “tipado” pillado por los pelos, si no es tipado en absoluto, o es tipado con otra filosofía y mentalidad, depende de vosotros (…probablemente sea todo a la vez)

  1. Flexibilidad extrema: Puedes crear clases nuevas que funcionen con código antiguo sin necesidad de modificar jerarquías de herencia complejas.
  2. Menos código (Boilerplate): No necesitas escribir interfaces gigantescas ni casteos constantes.
  3. Polimorfismo implícito: Las cosas simplemente funcionan juntas si “hablan el mismo idioma”.
  1. Errores en tiempo de ejecución: Si pasas un objeto que no tiene el método esperado, el programa explotará mientras se ejecuta (llega al usuario), no mientras compilas.
  2. Peor ayuda del IDE: El editor de código a veces no sabe qué métodos sugerirte porque no sabe qué tipo de objeto le va a llegar realmente.
  3. Refactorización más difícil: Es más difícil encontrar todos los lugares donde se usa un método si no hay un tipo explícito que los vincule.

Un ejemplo práctico en Python

Python es el rey del Duck Typing. Vamos a ver un ejemplo muy claro. Imagina que tenemos una función que hace “volar” cosas.

class Pato:
    def volar(self):
        print("El pato aletea y vuela")

class Avion:
    def volar(self):
        print("El avión enciende motores y despega")

class Ballena:
    def nadar(self):
        print("La ballena nada")

def hacer_volar(cosa):
    # Aquí está la magia: No comprobamos el tipo.
    # Solo intentamos ejecutar el método .volar()
    cosa.volar()

pato = Pato()
avion = Avion()
ballena = Ballena()

hacer_volar(pato)   # Funciona: El pato aletea...
hacer_volar(avion)  # Funciona: El avión enciende...
hacer_volar(ballena) # ERROR: AttributeError: 'Ballena' object has no attribute 'volar'

Copied!

Fíjate en lo que ha pasado. Pato y Avion no tienen ninguna relación entre ellos. No heredan de una clase común Volador. No implementan una interfaz común.

Sin embargo, la función hacer_volar los acepta a ambos. ¿Por qué? Porque ambos tienen un método volar. Caminan como un pato.

La Ballena, al no tener ese método, falla en tiempo de ejecución.

Duck Typing en JavaScript

JavaScript funciona igual. Al ser un lenguaje basado en prototipos y dinámico, podemos pasar cualquier objeto a una función.

function imprimirLongitud(elemento) {
    // Duck typing: Asumimos que 'elemento' tiene una propiedad 'length'
    console.log(elemento.length);
}

imprimirLongitud("Hola"); // 4 (Los strings tienen .length)
imprimirLongitud([1, 2, 3]); // 3 (Los arrays tienen .length)
imprimirLongitud({ length: 10 }); // 10 (Un objeto cualquiera con propiedad .length)
imprimirLongitud(50); // undefined (Los números no tienen .length)

Copied!

Aquí vemos el potencial del concepto. La función imprimirLongitud funciona con cadenas, con arrays. E incluso con un objeto inventado por nosotros, simplemente con que tenga la propiedad length.

¿Y en lenguajes estáticos como C#?

Tradicionalmente, en C# o Java, para lograr esto necesitaríamos usar Interfaces.

// En C# estricto (Forma tradicional)
interface IVolador {
    void Volar();
}

public void HacerVolar(IVolador cosa) { ... }

Copied!

Esto no es Duck Typing. Esto es polimorfismo por contratos. Aquí obligamos a las clases a firmar un contrato (IVolador).

Para hacer algo “realmente parecido” al Duck Typing en lenguajes estáticos, existen algunas soluciones.

Pero, en general, el Duck Typing es camino natural en Python/JS, pero un “invitado especial” (y no muy bien recibido) en lenguajes como C++, Java o C#.