惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
V2EX - 技术
V2EX - 技术
P
Privacy & Cybersecurity Law Blog
T
The Exploit Database - CXSecurity.com
美团技术团队
WordPress大学
WordPress大学
博客园 - 司徒正美
S
Securelist
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - Franky
Attack and Defense Labs
Attack and Defense Labs
Security Latest
Security Latest
L
LINUX DO - 最新话题
NISL@THU
NISL@THU
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
Y
Y Combinator Blog
The Hacker News
The Hacker News
Security Archives - TechRepublic
Security Archives - TechRepublic
IT之家
IT之家
T
Threatpost
Hugging Face - Blog
Hugging Face - Blog
Scott Helme
Scott Helme
S
SegmentFault 最新的问题
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42
B
Blog
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
小众软件
小众软件
V
Vulnerabilities – Threatpost
J
Java Code Geeks
V
Visual Studio Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
博客园 - 【当耐特】
Microsoft Security Blog
Microsoft Security Blog
S
Security @ Cisco Blogs
雷峰网
雷峰网
Help Net Security
Help Net Security
The Last Watchdog
The Last Watchdog
Recent Announcements
Recent Announcements
G
Google Developers Blog
C
CERT Recently Published Vulnerability Notes
T
Troy Hunt's Blog
MyScale Blog
MyScale Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Patrones de Diseño: El Lenguaje Universal que Todo Developer Debería Hablar
jesus manriq · 2026-05-16 · via DEV Community

Patrones de Diseño — Header


La primera vez que leí sobre patrones de diseño fue en un PDF escaneado del libro del Gang of Four. Duré 15 páginas antes de cerrarlo. Demasiada teoría. Demasiado UML. Demasiado "¿esto cuándo lo voy a usar en mi vida?"

Avance rápido 8 años. Hoy reviso ese mismo libro y lo leo diferente. Ya viví los dolores que esos patrones resuelven. Ya escribí el código acoplado que el Strategy desacopla, la clase de 3,000 líneas que el Observer parte en pedazos, el switch statement infinito que el Factory Method elimina.

Los patrones de diseño no se aprenden leyendo — se aprenden sufriendo. Pero si este artículo te ahorra aunque sea un dolor de cabeza, ya valió la pena escribirlo.


¿Qué es un patrón de diseño?

La definición formal dice que un patrón de diseño es una solución comprobada a un problema recurrente en el diseño de software. Suena corporativo. En español real: es un truco elegante que alguien más ya descubrió, ya probó, y ya documentó para que tú no tengas que reinventarlo a las 2 AM.

No son librerías. No son código que copias y pegas. Son más bien como planos: entiendes la idea, la adaptas a tu contexto, y la implementas en tu lenguaje.

Piénsalo así: los arquitectos no diseñan cada edificio desde cero. Tienen soluciones probadas para cosas como "cómo iluminar un pasillo interior" o "cómo drenar un techo plano." Los patterns son eso mismo, pero para software.


¿Por qué deberían importarte?

1. Te dan vocabulario

Imagina esta conversación sin patrones:

—"Oye, necesito que para la conexión a la base de datos crees una clase que no permita que nadie haga new desde fuera, que tenga un método estático que devuelva siempre la misma instancia verificando si ya existe, y que almacene esa instancia en un campo privado para que todo el sistema use exactamente el mismo objeto."

Con patrones:

—"Usa un Singleton para la conexión a la BD."

Tres palabras. El que habla no tuvo que explicar la estructura. El que escucha no tuvo que pedir un diagrama. Ambos visualizan inmediatamente la misma solución.

Los patrones son el equivalente en software de lo que "acorde de Do mayor" es para los músicos. No tienes que decir "pon el dedo índice en el primer traste de la segunda cuerda, el dedo medio en..." — dices "Do mayor" y todos saben qué tocar.

Ese vocabulario compartido es lo que permite que un equipo de 5 developers discuta una arquitectura compleja en 20 minutos en vez de 3 horas. Y es lo que hace que cuando lees "esta clase usa Observer" en un README, entiendas el 60% de su diseño antes de abrir el archivo.

2. Son soluciones déjà vu

El 90% de los problemas de diseño que enfrentas ya los vivió alguien en 1994. Esos problemas están tan estudiados que las soluciones tienen nombre, ejemplos, trade-offs documentados y libros enteros dedicados a cada uno.

3. Te vuelven mejor revisor de código

Conocer patrones no solo te ayuda a escribir mejor — te ayuda a LEER mejor. Cuando abres un codebase ajeno y reconoces un Observer en el event system, un Factory en el DI container, o un Decorator en los middleware, entiendes la arquitectura a la primera. Dejas de leer línea por línea y empiezas a ver la forma del código.

4. Son el puente entre junior y senior

Hay muchas diferencias entre un junior y un senior, pero una de las más notorias es esta: un junior resuelve un problema pensando en el presente inmediato. Un senior reconoce la forma del problema, recuerda qué patrón lo resuelve, y lo aplica sabiendo cómo escalará en 6 meses. Los patrones son atajos de experiencia.


Los 3 grandes grupos

Los 23 patrones clásicos del Gang of Four se agrupan en tres familias según su propósito:

Patrones Creacionales (5)

Factory Method, Abstract Factory, Builder, Prototype, Singleton

Responden a una pregunta: ¿cómo creo objetos sin acoplarme a clases concretas? Encapsulan la lógica de instanciación para que tu código dependa de interfaces, no de new ClaseConcreta().

Patrones Estructurales (7)

Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy

Responden a: ¿cómo ensamblo objetos y clases manteniendo la flexibilidad? Te ayudan a componer piezas sin que todo explote cuando agregas una nueva.

Patrones de Comportamiento (11)

Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor

Responden a: ¿cómo organizo la comunicación y responsabilidades entre objetos? Son los que más vas a usar en el día a día.


Ejemplos reales en Java y TypeScript

No voy a explicar los 23. Eso sería un libro. Voy a mostrarte los 3 que más aparecen en producción, con código que puedes correr.

1. Singleton — Una instancia, un punto de acceso global

Cuándo usarlo: Cuando necesitas exactamente una instancia de una clase. Conexiones a base de datos, configuraciones globales, sistemas de logging, caches.

Java:

public class DatabaseConnection {
    // volatile para visibilidad entre threads
    private static volatile DatabaseConnection instance;
    private final String connectionString;

    // Constructor privado — nadie puede hacer new
    private DatabaseConnection() {
        this.connectionString = "jdbc:postgresql://localhost:5432/mydb";
        System.out.println("🔌 Conectando a " + connectionString);
    }

    // Doble chequeo para thread-safety sin overhead de synchronized cada vez
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            synchronized (DatabaseConnection.class) {
                if (instance == null) {
                    instance = new DatabaseConnection();
                }
            }
        }
        return instance;
    }

    public void query(String sql) {
        System.out.println("⚡ Ejecutando: " + sql);
    }
}

// Uso
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
System.out.println(db1 == db2); // true — misma instancia

Enter fullscreen mode Exit fullscreen mode

TypeScript:

class ConfigManager {
    private static instance: ConfigManager;
    private config: Record<string, string> = {};

    private constructor() {
        this.config = {
            apiUrl: "https://api.guayoyo.tech",
            maxRetries: "3",
        };
        console.log("⚙️  Configuración cargada");
    }

    static getInstance(): ConfigManager {
        if (!ConfigManager.instance) {
            ConfigManager.instance = new ConfigManager();
        }
        return ConfigManager.instance;
    }

    get(key: string): string | undefined {
        return this.config[key];
    }

    set(key: string, value: string): void {
        this.config[key] = value;
    }
}

// Uso
const config1 = ConfigManager.getInstance();
const config2 = ConfigManager.getInstance();
console.log(config1 === config2);      // true
console.log(config1.get("apiUrl"));    // "https://api.guayoyo.tech"

Enter fullscreen mode Exit fullscreen mode

⚠️ Cuándo NO usarlo: El Singleton es el patrón más abusado. No lo uses para todo. Si tu clase tiene estado mutable que cambia con el tiempo, un Singleton puede ser un dolor de cabeza en testing y en aplicaciones multi-hilo. Úsalo cuando realmente necesites UNA y solo UNA instancia.


2. Strategy — Algoritmos intercambiables

Cuándo usarlo: Cuando tienes múltiples formas de hacer algo y quieres poder cambiarlas en tiempo de ejecución. Procesadores de pago, estrategias de descuento, distintos algoritmos de compresión, filtros de búsqueda.

Escenario real: procesador de pagos que acepta múltiples métodos.

Java:

// Interfaz común
interface PaymentStrategy {
    void pay(double amount);
}

// Estrategias concretas
class CardPayment implements PaymentStrategy {
    private final String cardNumber;

    public CardPayment(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public void pay(double amount) {
        System.out.printf("💳 Pagando $%.2f con tarjeta %s%n",
            amount, maskCard(cardNumber));
    }

    private String maskCard(String number) {
        return "****-****-****-" + number.substring(number.length() - 4);
    }
}

class CryptoPayment implements PaymentStrategy {
    private final String wallet;

    public CryptoPayment(String wallet) {
        this.wallet = wallet;
    }

    @Override
    public void pay(double amount) {
        System.out.printf("₿ Pagando $%.2f con wallet %s%n",
            amount, wallet.substring(0, 8) + "...");
    }
}

class BankTransferPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.printf("🏦 Transfiriendo $%.2f vía transferencia bancaria%n",
            amount);
    }
}

// Contexto — usa la estrategia pero no sabe los detalles
class PaymentProcessor {
    private PaymentStrategy strategy;

    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void process(double amount) {
        if (strategy == null) {
            throw new IllegalStateException("Selecciona un método de pago");
        }
        strategy.pay(amount);
    }
}

// Uso
var processor = new PaymentProcessor();

processor.setStrategy(new CardPayment("4532123456789012"));
processor.process(149.99);
// 💳 Pagando $149.99 con tarjeta ****-****-****-9012

processor.setStrategy(new CryptoPayment("0x71C7656EC7ab88b098defB751B7401B5f6d8976F"));
processor.process(149.99);
// ₿ Pagando $149.99 con wallet 0x71C765...

Enter fullscreen mode Exit fullscreen mode

TypeScript:

// Interfaz (en TS podemos usar type o interface)
interface PaymentStrategy {
    pay(amount: number): void;
    methodName: string;
}

// Estrategias concretas
class CardPayment implements PaymentStrategy {
    methodName = "Tarjeta";

    constructor(private cardNumber: string) {}

    pay(amount: number): void {
        console.log(
            `💳 Pagando $${amount.toFixed(2)} con tarjeta ****-${this.cardNumber.slice(-4)}`
        );
    }
}

class PagoMovilPayment implements PaymentStrategy {
    methodName = "Pago Móvil";

    constructor(private phone: string, private bank: string) {}

    pay(amount: number): void {
        console.log(
            `📱 Pagando $${amount.toFixed(2)} vía Pago Móvil (${this.bank}, ${this.phone})`
        );
    }
}

class CryptoPayment implements PaymentStrategy {
    methodName = "Cripto";

    constructor(private wallet: string) {}

    pay(amount: number): void {
        console.log(
            `₿ Pagando $${amount.toFixed(2)} con wallet ${this.wallet.slice(0, 8)}...`
        );
    }
}

// Contexto
class PaymentProcessor {
    private strategy: PaymentStrategy | null = null;

    setStrategy(strategy: PaymentStrategy): void {
        this.strategy = strategy;
    }

    process(amount: number): void {
        if (!this.strategy) {
            throw new Error("Selecciona un método de pago");
        }
        console.log(`\n🧾 Procesando factura por: $${amount.toFixed(2)}`);
        this.strategy.pay(amount);
    }
}

// Uso
const processor = new PaymentProcessor();

processor.setStrategy(new CardPayment("4532123456789012"));
processor.process(149.99);

processor.setStrategy(
    new PagoMovilPayment("0414-0108660", "Banco de Venezuela")
);
processor.process(89.50);

processor.setStrategy(
    new CryptoPayment("0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
);
processor.process(200.00);

Enter fullscreen mode Exit fullscreen mode

La belleza del Strategy: agregar un nuevo método de pago (PayPal, Zelle, Efectivo) es crear UNA clase nueva. No tocas el PaymentProcessor. No modificas código que ya funciona. Eso es el Open/Closed Principle en acción — abierto para extensión, cerrado para modificación.


3. Observer — Reaccionar a eventos sin acoplamiento

Cuándo usarlo: Cuando un cambio de estado en un objeto debe notificar a muchos otros automáticamente. Notificaciones, event systems, sincronización de UI, WebSocket handlers, arquitecturas pub/sub.

Escenario real: un sistema de monitoreo de servidores.

Java:

import java.util.*;

// Interfaz del suscriptor
interface ServerObserver {
    void update(String serverId, String event);
}

// Notificador (Publisher)
class ServerMonitor {
    private final Map<String, List<ServerObserver>> subscriptions = new HashMap<>();

    public void subscribe(String serverId, ServerObserver observer) {
        subscriptions
            .computeIfAbsent(serverId, k -> new ArrayList<>())
            .add(observer);
    }

    public void unsubscribe(String serverId, ServerObserver observer) {
        List<ServerObserver> obs = subscriptions.get(serverId);
        if (obs != null) obs.remove(observer);
    }

    public void alert(String serverId, String event) {
        System.out.printf("🔔 [%s] %s%n", serverId, event);
        List<ServerObserver> obs = subscriptions.get(serverId);
        if (obs != null) {
            for (ServerObserver observer : obs) {
                observer.update(serverId, event);
            }
        }
    }
}

// Suscriptores concretos
class SlackNotifier implements ServerObserver {
    @Override
    public void update(String serverId, String event) {
        System.out.printf("  📨 Slack → #oncall: Servidor %s — %s%n", serverId, event);
    }
}

class DatabaseLogger implements ServerObserver {
    @Override
    public void update(String serverId, String event) {
        System.out.printf("  🗄️  DB → INSERT INTO alerts (server, event, timestamp) "
            + "VALUES ('%s', '%s', NOW())%n", serverId, event);
    }
}

class AutoScaler implements ServerObserver {
    @Override
    public void update(String serverId, String event) {
        if (event.contains("CPU > 90%")) {
            System.out.printf("  ⚡ AutoScaler → Escalando servidor %s%n", serverId);
        }
    }
}

// Uso
var monitor = new ServerMonitor();

monitor.subscribe("prod-web-01", new SlackNotifier());
monitor.subscribe("prod-web-01", new DatabaseLogger());
monitor.subscribe("prod-web-01", new AutoScaler());

monitor.alert("prod-web-01", "CPU > 90% por 2 minutos");
// 🔔 [prod-web-01] CPU > 90% por 2 minutos
//   📨 Slack → #oncall: Servidor prod-web-01 — CPU > 90% por 2 minutos
//   🗄️  DB → INSERT INTO alerts (...)
//   ⚡ AutoScaler → Escalando servidor prod-web-01

Enter fullscreen mode Exit fullscreen mode

TypeScript:

// Interfaces
interface ServerObserver {
    update(serverId: string, event: string): void;
}

// Notificador
class ServerMonitor {
    private subscriptions = new Map<string, ServerObserver[]>();

    subscribe(serverId: string, observer: ServerObserver): void {
        const existing = this.subscriptions.get(serverId) ?? [];
        existing.push(observer);
        this.subscriptions.set(serverId, existing);
    }

    unsubscribe(serverId: string, observer: ServerObserver): void {
        const observers = this.subscriptions.get(serverId);
        if (observers) {
            this.subscriptions.set(
                serverId,
                observers.filter((obs) => obs !== observer)
            );
        }
    }

    alert(serverId: string, event: string): void {
        console.log(`🔔 [${serverId}] ${event}`);
        const observers = this.subscriptions.get(serverId);
        if (observers) {
            observers.forEach((obs) => obs.update(serverId, event));
        }
    }
}

// Suscriptores
class SlackNotifier implements ServerObserver {
    update(serverId: string, event: string): void {
        console.log(
            `  📨 Slack → #oncall: Servidor ${serverId}${event}`
        );
    }
}

class PagerDutyNotifier implements ServerObserver {
    update(serverId: string, event: string): void {
        if (event.includes("CRÍTICO")) {
            console.log(
                `  🚨 PagerDuty → ESCALANDO: ${serverId} requiere atención inmediata`
            );
        }
    }
}

class MetricsLogger implements ServerObserver {
    update(serverId: string, event: string): void {
        const timestamp = new Date().toISOString();
        console.log(
            `  📊 Metrics → { server: "${serverId}", event: "${event}", ts: "${timestamp}" }`
        );
    }
}

// Uso en un sistema de monitoreo real
const monitor = new ServerMonitor();

monitor.subscribe("prod-api-03", new SlackNotifier());
monitor.subscribe("prod-api-03", new PagerDutyNotifier());
monitor.subscribe("prod-api-03", new MetricsLogger());

monitor.alert("prod-api-03", "CRÍTICO: Disco al 98%");

Enter fullscreen mode Exit fullscreen mode

El Observer es ubicuo en desarrollo moderno: Redux es Observer. Los WebSocket handlers son Observer. Los event listeners del DOM son Observer. Entender el patrón te hace entender media docena de librerías y frameworks automáticamente.


Cómo empezar sin abrumarte

No intentes memorizar los 23 patrones. Es inútil. Mejor:

  1. Aprende los 5 esenciales primero: Singleton, Factory Method, Strategy, Observer y Decorator. Cubren el 80% de los casos reales.

  2. Identifícalos en tu stack: Spring Boot está lleno de Template Method y Proxy. Angular usa Observer a través de RxJS. React depende de Observer con su Virtual DOM. Node.js usa Strategy en los middlewares de Express. Encuéntralos en lo que YA usas.

  3. Practica la refactorización inversa: Toma código viejo que huele mal — una clase de 500 líneas, un if/else de 20 ramas — y pregúntate: ¿qué patrón podría domesticar esto? Luego refactoriza aplicándolo.

  4. Lee Refactoring.Guru: Es el mejor recurso gratuito que existe. Explicaciones visuales, pseudocódigo, ejemplos en 10 lenguajes y cero academicismo innecesario. Si solo tomas una recomendación de este artículo, que sea esa: refactoring.guru/es

  5. No conviertas todo en un patrón: Este es el error del recién convertido. Empiezas a ver Singletons y hay Singletons hasta en la sopa. El patrón debe simplificar, no complicar. Si aplicar un patrón hace tu código más difícil de leer, no lo apliques. Simple > Elegante.


Conclusión

Los patrones de diseño no te hacen mejor programador por saber sus nombres. Te hacen mejor programador porque te enseñan a pensar en estructuras, en responsabilidades, en consecuencias a largo plazo.

Son el equivalente en código de saber jugadas de ajedrez en lugar de mover piezas viendo qué pasa. Puedes jugar sin conocerlas. Pero conocidas, el juego se vuelve diferente.

Y lo mejor: una vez que empiezas a reconocer patrones, dejas de ver código. Empiezas a ver formas, intenciones, estructura. El código de otros — y el tuyo — se lee con otros ojos.


¿Ya usas patrones de diseño en tu día a día sin darte cuenta? ¿Cuál fue el primero que aprendiste? Cuéntamelo. El mío fue Singleton — y lo usé mal por dos años hasta que entendí cuándo NO usarlo.