Regístrate para acceder a más de 15 cursos gratuitos de programación con un simulador

Null Object Pattern Python: Polimorfismo

Al crear aplicaciones en Python, especialmente cuando hay usuarios que inician sesión 👤, a veces nos encontramos con objetos que pueden no existir, como el usuario actual en una web.

Hoy aprenderemos a manejar estos casos de forma sencilla con el patrón de diseño Null Object.


¿Cuál es el problema que intentamos resolver?

Imagina que estamos trabajando en un sistema con un usuario actual. Ese usuario puede estar en dos estados:

✅👤 Autenticado: El usuario ha iniciado sesión, entonces tenemos un objeto con su información.

❌👤 No autenticado: El usuario no ha iniciado sesión, por lo que no tenemos un objeto usuario para usar.

El problema es: ¿Cómo manejar el sistema cuando no hay un usuario autenticado? 🤷‍♂️

Imagina este código:

if is_authenticated and current_user.has_articles():
    for article in current_user.get_articles():
        # Mostrar artículos

Este current_user solo existe si el usuario ya se autenticó. Si no verificamos antes y tratamos de hacer algo como current_user.has_articles(), se puede lanzar un error porque estaríamos llamando un método sobre un valor None.

Hasta ahí es algo fácil de evitar con un par de if. Pero… ¿qué pasa si tenemos muchas de esas verificaciones por todo el código? Se vuelve un desastre mantener eso.


¿Qué es el patrón Null Object?

Este patrón de diseño nos propone una idea interesante: en lugar de tener None cuando un objeto está ausente, ¿por qué no tener un objeto que actúe como si no existiera, pero igual tenga los métodos necesarios?

Es decir, en vez de hacer muchas verificaciones para ver si un objeto existe, simplemente devolvemos un objeto especial que "se comporte como si no hiciera nada".

Por ejemplo, un usuario invitado (no autenticado) puede ser representado por un objeto llamado Guest.


Caso práctico: sistema de usuarios

Vamos a ver cómo aplicar esto en la vida real.

Paso 1: Definimos la clase User

class User:
    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name

    def has_articles(self):
        return True

    def get_articles(self):
        # Imaginemos que consulta la base de datos
        return ["article1", "article2", "article3"]

Este sería el usuario normal, autenticado, con nombre y artículos.

Paso 2: Creamos la clase Guest

Este es el objeto que representará al "usuario ausente". Pero aún así tendrá los mismos métodos para que el resto del código no tenga que preocuparse.

class Guest:
    def get_name(self):
        return "Guest"

    def has_articles(self):
        return False

    def get_articles(self):
        return []

Fíjate que Guest tiene los mismos métodos: get_name(), has_articles() y get_articles(). La clave está en que no devuelve errores, sino valores por defecto: un nombre genérico, False, y una lista vacía.


Función para obtener el usuario actual

Este es otro punto importante: vamos a centralizar la lógica para traernos al usuario actual desde algún lado (como una sesión o token). Si no hay un usuario autenticado, devolvemos un Guest.

def get_current_user(authenticated):
    if authenticated:
        return User("Alice")
    else:
        return Guest()

Veamos cómo se comporta este patrón:

current_user = get_current_user(authenticated=True)
print(current_user.get_name())       # Alice
print(current_user.get_articles())  # ['article1', 'article2', 'article3']

current_user = get_current_user(authenticated=False)
print(current_user.get_name())       # Guest
print(current_user.get_articles())  # []

En los dos casos, el código funciona igual. Nunca pregunta si el usuario está autenticado. Eso es lo más valioso: nos quitamos un montón de if repetidos por todas partes.

Aplicar Null Object en un bloque real de código

Gracias a este patrón, ahora podemos escribir código que trabaje con usuarios sin necesidad de preguntarle al sistema si el usuario está o no autenticado.

current_user = get_current_user(authenticated=False)

if current_user.has_articles():
    for article in current_user.get_articles():
        print(article)

Esta parte siempre funciona porque tanto User como Guest tienen los métodos necesarios. No más errores por tratar de acceder a métodos sobre None.

Tabla comparativa

Situación Sin Null Object Con Null Object
Usuario no autenticado Retorna None, hay que verificarlo Devuelve un Guest con métodos
Hay que usar if extra Sí, en todo el código No, una sola vez al crear el objeto
Posibilidad de errores Alta (por no verificar None) Baja
Código repetido Mucho Poco

¿Cuándo usar este patrón?

Usarlo es muy útil cuando:

  • Tienes que llamar métodos sobre objetos que podrían no existir.
  • Quieres evitar escribir if objeto is not None en todas partes.
  • Todos los objetos tienen la misma interfaz.

⚠️: Pero ojo: no hay que usarlo por todo. Si exageramos, podemos terminar ocultando errores importantes o confundir a quien le toque leer el código.

Código completo de ejemplo

class User:
    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name

    def has_articles(self):
        return True

    def get_articles(self):
        return ["article1", "article2", "article3"]

class Guest:
    def get_name(self):
        return "Guest"

    def has_articles(self):
        return False

    def get_articles(self):
        return []

def get_current_user(authenticated):
    if authenticated:
        return User("Alice")
    else:
        return Guest()

# Uso en el sistema
current_user = get_current_user(authenticated=False)

if current_user.has_articles():
    for article in current_user.get_articles():
        print(article)



Resumen

  • En sistemas reales, muchas veces tenemos que trabajar con objetos que pueden no existir.
  • El patrón Null Object nos ayuda a manejar estos casos devolviendo un objeto "vacío", pero funcional.
  • Esto evita errores por objetos nulos (None) y simplifica el código.
  • La clave es que tanto el objeto real como el Null Object compartan la misma interfaz (mismos métodos).
  • Úsalo cuando tengas muchos casos repetidos de verificación y lógica duplicada.

Para acceder completo a curso necesitas un plan básico

El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.

Obtener acceso
130
cursos
1000
ejercicios
2000+
horas de teoría
3200
test

Obtén acceso

Cursos de programación para principiantes y desarrolladores experimentados. Comienza tu aprendizaje de forma gratuita

  • 130 cursos, 2000+ horas de teoría
  • 1000 ejercicios prácticos en el navegador
  • 360 000 estudiantes
Al enviar el formulario, aceptas el «Política de privacidad» y los términos de la «Oferta», y también aceptas los «Términos y condiciones de uso»

Nuestros graduados trabajan en empresas como:

Bookmate
Health Samurai
Dualboot
ABBYY