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.