- ¿Cuál es el problema que intentamos resolver?
- ¿Qué es el patrón Null Object?
- Caso práctico: sistema de usuarios
- Función para obtener el usuario actual
- Aplicar Null Object en un bloque real de código
- Tabla comparativa
- ¿Cuándo usar este patrón?
- Código completo de ejemplo
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 Noneen 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.