Sobes.tech
Middle+

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.