Sobes.tech
Middle+

Cuéntame sobre el diseño de patrones que has utilizado en tu desarrollo.

sobes.tech AI

Respuesta de la IA

Utilicé los siguientes patrones de diseño:

  • MVC (Modelo-Vista-Controlador): Separación de la lógica de la aplicación. El modelo gestiona los datos, la vista se encarga de la presentación y el controlador procesa la interacción del usuario y actualiza el modelo o la vista. Lo apliqué en Angular, donde los componentes implementan parcialmente esta ideología.

  • MVVM (Modelo-Vista-ViewModel): Similar a MVC, pero introduce el ViewModel como intermediario entre la vista y el modelo. El ViewModel contiene la lógica de negocio para mostrar datos y manejar acciones del usuario. Lo usé activamente con React y Vuex (Vue), donde el estado de la aplicación se gestiona de manera centralizada.

    // Ejemplo de reactividad en React usando useState
    import React, { useState } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0); // useState parte del mecanismo MVVM
    
      const increment = () => {
        setCount(count + 1); // Actualización del estado mediante el setter
      };
    
      return (
        <div>
          <p>Count: {count}</p> {/* Mostrar estado */}
          <button onClick={increment}>Increment</button> {/* Acción que afecta al estado */}
        </div>
      );
    }
    
  • Patrón de componentes: División de la interfaz de usuario en componentes independientes y reutilizables. Cada componente encapsula su lógica, marcado y estilos. Es la base de frameworks modernos como React, Vue y Angular.

    // Ejemplo de un componente React simple
    function Button(props) {
      return (
        <button className="my-button" onClick={props.onClick}>
          {props.label}
        </button>
      );
    }
    
  • Adaptador (Adapter): Transforma la interfaz de una clase en otra que los clientes esperan. Permite que clases con interfaces incompatibles trabajen juntas. Lo apliqué para integrar bibliotecas o APIs externas con diferentes firmas de métodos.

    // Ejemplo de adaptador para trabajar con diferentes clientes API
    class OldApi {
      getDataOld(id) {
        console.log(`Obteniendo datos de la API antigua para id ${id}`);
        return { id: id, name: `Item ${id}` };
      }
    }
    
    class NewApi {
      fetchDataNew(itemId) {
        console.log(`Obteniendo datos de la API nueva para id de ítem ${itemId}`);
        return { itemId: itemId, label: `Producto ${itemId}` };
      }
    }
    
    class ApiAdapter {
      constructor(apiClient) {
        this.apiClient = apiClient;
      }
    
      getData(id) {
        if (this.apiClient instanceof OldApi) {
          return this.apiClient.getDataOld(id);
        } else if (this.apiClient instanceof NewApi) {
          // Transformación de datos del nuevo formato
          const newData = this.apiClient.fetchDataNew(id);
          return { id: newData.itemId, name: newData.label };
        }
        throw new Error("Cliente API no soportado");
      }
    }
    
    const oldApiAdapter = new ApiAdapter(new OldApi());
    console.log(oldApiAdapter.getData(1));
    
    const newApiAdapter = new ApiAdapter(new NewApi());
    console.log(newApiAdapter.getData(2));
    
  • Iterador (Iterator): Proporciona una forma de acceder secuencialmente a los elementos de un objeto compuesto sin revelar su representación interna. Se usa en bucles (for...of) y métodos de arrays (forEach, map, filter).

    // Ejemplo de uso de iterador en JavaScript
    const numbers = [1, 2, 3, 4, 5];
    
    // Obtención explícita del iterador
    const iterator = numbers[Symbol.iterator]();
    
    console.log(iterator.next()); // { value: 1, done: false }
    console.log(iterator.next()); // { value: 2, done: false }
    // ...
    console.log(iterator.next()); // { value: undefined, done: true }
    
    // Uso del iterador en for...of
    for (const number of numbers) {
      console.log(number);
    }
    
  • Decorador (Decorator): Añade dinámicamente nuevas responsabilidades a un objeto, permitiendo ampliar su funcionalidad sin modificar su estructura. Lo usé en forma de HOC (Componentes de Orden Superior) en React o decoradores en Angular/TypeScript.

    // Ejemplo de HOC en React
    function withLogging(WrappedComponent) {
      return function WithLogging(props) {
        console.log(`Componente ${WrappedComponent.name} renderizado con props:`, props);
        return <WrappedComponent {...props} />;
      };
    }
    
    // Uso del HOC
    const EnhancedButton = withLogging(Button);
    
    // Uso del componente mejorado
    // <EnhancedButton label="Haz clic" onClick={() => console.log('¡Clic!')} />
    
  • Singleton (Odiado): Garantiza que una clase tenga una sola instancia y proporciona un punto de acceso global a ella. Lo apliqué para gestionar el estado global de la aplicación o crear una instancia única de un servicio (por ejemplo, servicio de registro, configuración).

    // Ejemplo del patrón Singleton
    class ConfigManager {
      constructor() {
        if (ConfigManager.instance) {
          return ConfigManager.instance;
        }
        this.settings = {}; // Inicialización de configuraciones
        ConfigManager.instance = this;
      }
    
      setSetting(key, value) {
        this.settings[key] = value;
      }
    
      getSetting(key) {
        return this.settings[key];
      }
    }
    
    const config1 = new ConfigManager();
    const config2 = new ConfigManager();
    
    console.log(config1 === config2); // true (la misma instancia)
    
    config1.setSetting('apiUrl', 'https://api.ejemplo.com');
    console.log(config2.getSetting('apiUrl')); // https://api.ejemplo.com
    
  • Método de fábrica (Factory Method): Define una interfaz para crear un objeto, pero permite a las subclases decidir qué clase instanciar. Lo usé para crear diferentes tipos de elementos de interfaz de usuario u objetos basados en la configuración.

  • Estrategia (Strategy): Define una familia de algoritmos, encapsula cada uno y los hace intercambiables. Permite que el algoritmo varíe independientemente de los clientes que lo usan. Lo usé para implementar diferentes enfoques para ordenar datos, validar formularios o manejar eventos.

El uso de estos patrones ayudó a crear un código más estructurado, modular, testeable y mantenible.