Ir al contenido

Práctica asincrónica · S7 V · viernes 26 de junio

Práctica: Adapter vs Facade

Cuatro ejercicios para decidir, no para leer. Dado un código que huele, elegís si va Adapter o Facade (y por qué), lo refactorizás en un editor PHP real, y recién después revelás la solución. Uno es trampa — un caso donde no va ningún patrón. Distinguir los dos es justo lo que evalúa la Mini-entrega 4.

Cómo se trabaja cada ejercicio

  1. Decidí — escribí en el recuadro si ves Adapter, Facade o ninguno, antes de ver la respuesta. El veredicto está oculto a propósito.
  2. Refactorizá — abrí el código en php.cesar.sh, escribí tu solución y ejecutala de verdad.
  3. Compará — revelá el veredicto, la solución modelo y el “por qué”. ¿Acertaste el criterio?

La pregunta que separa los dos patrones: ¿el problema es la forma de UNO (Adapter), o la cantidad de cosas en orden de MUCHOS (Facade)? Esta práctica es el insumo directo para la Mini-entrega 4.

Adapter vs Facade

Cuatro casos: uno donde algo habla otro idioma (Adapter), uno donde el cliente coordina un subsistema (Facade), una trampa donde no va ningún patrón, y uno mixto donde los dos conviven en capas distintas.

Ejercicio 1 ¿qué aplica acá?

Una SDK de envíos de terceros (FedExSDK) expone requestQuote() y devuelve un array crudo (cost_cents, eta_days). Tu código quiere hablar en su propio idioma —$shipping->quote($weight): Quote— pero hoy llama directo a la SDK y mapea cost_cents a mano, regado en 3 controllers.

Tu turno ¿Adapter o Facade? Una sola clase ajena, con la interfaz equivocada, que no podés tocar. Decidí antes de refactorizar.
// librería de terceros — no la podés tocar
class FedExSDK
{
    public function requestQuote(array $pkg): array
    {
        // → ['cost_cents' => 1250, 'eta_days' => 3]
        return ['cost_cents' => 1250, 'eta_days' => 3];
    }
}

// tu código quiere hablar ASÍ:  $shipping->quote($weight): Quote
// pero hoy llama directo a requestQuote() y mapea cost_cents a mano,
// regado en 3 controllers distintos.
Mostrar pista

Una sola clase, útil, con la interfaz equivocada. ¿Qué patrón pone un traductor en medio sin tocar la clase ajena?

Ejercicio 2 ¿qué aplica acá?

Publicar un post toca cuatro servicios en orden: guardar en DB, indexar en el buscador, invalidar cache y avisar a seguidores. Cada servicio ya hace bien su trabajo — el problema es que el PostController conoce el orden, y ese mismo orden se repite en la API, el import masivo y el cron de programados.

Tu turno ¿Adapter o Facade? Cuatro servicios que ya hablan bien, pero el cliente conoce la coreografía y la repite. Decidí.
class PostController
{
    public function publish(Post $post): void
    {
        $this->db->save($post);          // 1. guardar
        $this->search->index($post);     // 2. indexar en buscador
        $this->cache->bust('posts');     // 3. invalidar cache
        $this->notifier->fanout($post);  // 4. avisar a seguidores
    }
}
// el MISMO orden se repite en: publicar por API, import masivo, cron de programados
Mostrar pista

Nadie habla otro idioma acá. El smell es el orden de muchos servicios, repetido en cada cliente. ¿Quién debería conocer esa coreografía?

Ejercicio 3 ¿qué aplica acá?

Una librería del banco expone BalanceClient::getBalance(string $account): float — una sola clase, una sola operación, que ya devuelve un float. Tu Wallet::show() llama a getBalance() y devuelve ese float directo, sin tocar nada. Alguien propone "envolvámoslo en un Adapter o una Facade para desacoplar".

Tu turno ¿Adapter o Facade? Pensalo bien: ¿hay interfaz incompatible? ¿hay varios servicios que coordinar? ¿O la respuesta es "ninguno"?
// librería del banco — una sola clase, una sola operación
class BalanceClient
{
    public function getBalance(string $account): float { /* ... */ return 0.0; }
}

// tu código:
class Wallet
{
    public function show(BalanceClient $c, string $acc): float
    {
        return $c->getBalance($acc);   // ya devuelve un float, justo lo que querés
    }
}
Mostrar pista

Adapter resuelve interfaz incompatible. Facade resuelve coordinar muchos. ¿Cuál de los dos problemas tenés acá... o ninguno?

Ejercicio 4 ¿qué aplica acá?

Un CheckoutController coordina tres subsistemas en orden (reservar stock, calcular total, notificar) y, en medio, cobra con una pasarela local —PuntoVentaSV::debitar()— que habla otro idioma (centavos, colones, resultado en español). Dos problemas distintos en el mismo método.

Tu turno ¿Adapter o Facade? Cuidado: acá puede que la respuesta sean LOS DOS, en capas distintas. ¿Dónde va cada uno?
class CheckoutController
{
    public function checkout(Cart $cart): void
    {
        $this->stock->reserve($cart);                       // subsistema
        $total = $this->pricing->total($cart);              // subsistema
        // y para cobrar, la pasarela local habla raro:
        $r = (new PuntoVentaSV())->debitar($total * 100, 'colones'); // ¡otro idioma!
        $ok = $r['resultado'] === 'OK';
        $this->notify->send($cart);                         // subsistema
    }
}
Mostrar pista

Hay dos problemas a la vez: un orden de subsistemas (¿qué patrón?) y una pasarela que habla raro metida en medio (¿qué otro patrón?).

Lo que sigue

Mini-entrega 4

Ya distinguís Adapter de Facade — y cuándo no va ninguno. Ahora aplicalo sobre el legacy real: pagos → Adapter (cada pasarela habla otro idioma) y checkout → Facade (placeOrder coordina 6 subsistemas). Vos detectás dónde encaja cada patrón — es parte del trabajo. Con bitácora de 4 preguntas por módulo.

Cierra jueves 2 de julio, 23:59 · 7% · branch me4 + PR a main + tag v4-adapter-facade

Ver la consigna completa en Moodle →