Fale sobre o design de padrões que você usou no seu desenvolvimento.
sobes.tech IA
Resposta da IA
Utilizei os seguintes padrões de design:
-
MVC (Model-View-Controller): Separação da lógica da aplicação. O Modelo gere os dados, a Visão é responsável pela apresentação, e o Controlador processa a interação do utilizador e atualiza o Modelo ou a Visão. Apliquei em Angular, onde os componentes implementam parcialmente esta ideologia.
-
MVVM (Model-View-ViewModel): Semelhante ao MVC, mas introduz o ViewModel como intermediário entre a Visão e o Modelo. O ViewModel contém a lógica de negócio para exibir dados e lidar com ações do utilizador. Usei ativamente com React e Vuex (Vue), onde o estado da aplicação é gerido de forma centralizada.
// Exemplo de reatividade em React usando useState import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); // useState faz parte do mecanismo MVVM const increment = () => { setCount(count + 1); // Atualização do estado via setter }; return ( <div> <p>Count: {count}</p> {/* Mostrar estado */} <button onClick={increment}>Increment</button> {/* Ação que afeta o estado */} </div> ); } -
Padrão de componentes: Dividir a interface de utilizador em componentes independentes e reutilizáveis. Cada componente encapsula a sua lógica, marcação e estilos. É a base de frameworks modernos como React, Vue e Angular.
// Exemplo de um componente React simples function Button(props) { return ( <button className="my-button" onClick={props.onClick}> {props.label} </button> ); } -
Adaptador (Adapter): Transforma a interface de uma classe numa outra que os clientes esperam. Permite que classes com interfaces incompatíveis trabalhem juntas. Apliquei para integrar bibliotecas ou APIs externas com assinaturas de métodos diferentes.
// Exemplo de adaptador para trabalhar com diferentes clientes API class OldApi { getDataOld(id) { console.log(`A obter dados da Old API para id ${id}`); return { id: id, name: `Item ${id}` }; } } class NewApi { fetchDataNew(itemId) { console.log(`A buscar dados da New API para id do item ${itemId}`); return { itemId: itemId, label: `Produto ${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) { // Transformar dados do novo formato const newData = this.apiClient.fetchDataNew(id); return { id: newData.itemId, name: newData.label }; } throw new Error("Cliente API não suportado"); } } const oldApiAdapter = new ApiAdapter(new OldApi()); console.log(oldApiAdapter.getData(1)); const newApiAdapter = new ApiAdapter(new NewApi()); console.log(newApiAdapter.getData(2)); -
Iterador (Iterator): Fornece uma forma de acesso sequencial aos elementos de um objeto composto sem revelar sua representação interna. Usado em loops (
for...of) e métodos de arrays (forEach,map,filter).// Exemplo de uso de iterador em JavaScript const numbers = [1, 2, 3, 4, 5]; // Obtenção explícita do 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 do iterador em for...of for (const number of numbers) { console.log(number); } -
Decorador (Decorator): Adiciona dinamicamente novas responsabilidades a um objeto, permitindo ampliar sua funcionalidade sem alterar sua estrutura. Usei na forma de HOC (Componentes de Ordem Superior) em React ou decoradores em Angular/TypeScript.
// Exemplo de HOC em React function withLogging(WrappedComponent) { return function WithLogging(props) { console.log(`Componente ${WrappedComponent.name} renderizado com props:`, props); return <WrappedComponent {...props} />; }; } // Uso do HOC const EnhancedButton = withLogging(Button); // Uso do componente aprimorado // <EnhancedButton label="Clique em mim" onClick={() => console.log('Clicado!')} /> -
O único (Singleton): Garante que uma classe tenha apenas uma instância e fornece um ponto de acesso global a ela. Apliquei para gerenciar o estado global da aplicação ou criar uma única instância de um serviço (por exemplo, serviço de logging, configuração).
// Exemplo do padrão Singleton class ConfigManager { constructor() { if (ConfigManager.instance) { return ConfigManager.instance; } this.settings = {}; // Inicialização de configurações 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 (mesma instância) config1.setSetting('apiUrl', 'https://api.exemplo.com'); console.log(config2.getSetting('apiUrl')); // https://api.exemplo.com -
Método de fábrica (Factory Method): Define uma interface para criar um objeto, mas permite às subclasses decidir qual classe instanciar. Apliquei para criar diferentes tipos de elementos de interface do utilizador ou objetos com base na configuração.
-
Estratégia (Strategy): Define uma família de algoritmos, encapsula cada um deles e os torna intercambiáveis. Permite que o algoritmo varie independentemente dos clientes que o utilizam. Usei para implementar diferentes abordagens para ordenar dados, validar formulários ou lidar com eventos.
O uso desses padrões ajudou a criar um código mais estruturado, modular, testável e de fácil manutenção.