Simulador de Circuitos Elétricos Residenciais - Análise de Projetos de Programação

Introdução

  1. Análise dos Conhecimentos Requiredos

Estes projetos abrangem múltiplos tópicos fundamentais:

  • Análise de Circuitos: Projeto e análise de dispositivos como interruptores, controladores de velocidade, lâmpadas e ventiladores, incluindo princípios de funcionamento, relações de entrada e saída, e estados operacionais.
  • Sistemas de Controle: Controle de dispositivos como interruptores, controladores de velocidade exclusivos mútuos, simulando relacionamentos entre controladores e dispositivos controlados como luzes e ventiladores.
  • Componentes Elétricos: Compreensão das características elétricas de cada componente - controle de corrente do interruptor, ajuste de tensão do controlador, brilho da lâmpada e velocidade do ventilador.
  • Implementação de Software: Uso de linguagem de programação para implementar dispositivos, lógica de controle, conversão de formato de entrada e saída, simulando conexões de circuito e atualização de estados.
  • Cálculos de Circuito: Cálculos de tensão, corrente e resistência, garantindo formatação correta da saída.
  1. Análise de Complexidade

Volume de Dados

Entrada: Inclui informações de conexão de dispositivos, instruções de controle, dados de circuitos série e paralelo. O comprimento varia conforme a complexidade do circuito a ser simulado.

Saída: Requer saída do estado ou parâmetros de cada dispositivo como estado do interruptor, brilho da luz, velocidade do ventilador. A saída deve ser ordenada por identificador de dispositivo com formato preciso.

Nível de Dificuldade

Muito Difícil: O problema envolve múltiplos componentes de circuitos e dispositivos de controle. Os desafios incluem:

  • Parsing correto de entradas, especialmente conexões entre dispositivos e mudanças de estado
  • Implementação de lógica de controle como mudanças de estado de interruptores e ajustes de velocidade
  • Processamento de cálculo de estado para diferentes componentes elétricos como ventiladores, lâmpadas e cortinsa
  • Cálculo e formatação de resultados de saída, garantindo precisão e requisitos de formato

Design e Análise do Sistema

  1. Arquitetura do Sistema - Versão 3

1.1 Design de Classes

1.2 Métricas do Código

  • Arquivo Principal: PTA7.java
  • Linhas de Código: 1.052 linhas
  • Total de Declarações: 629 declarações
  • Porcentagem de Declarações de Ramificação: 21,0%
  • Declarações de Chamadas de Método: 184 declarações
  • Comentários: 4,8% das linhas
  • Classes e Interfaces: 8 classes
  • Métodos por Classe: média de 12,38 métodos por classe
  • Declarações por Método: média de 6,29 declarações por método
  • Complexidade Máxima: 8
  • Profundidade Máxima de Bloco: 8

1.3 Análise de Design

1.3.1 Visão Geral

O sistema simula vários dispositivos elétricos em um circuito, processando comandos de entrada para construir a estrutura do circuito, executar operações de dispositivos e calcular distribuição de tensão e estado dos dispositivos. O sistema consiste em:

  • Padrão de Factory: Para criação de dispositivos
  • Padrão Composite: Para gerenciamento de componentes de circuito
  • Hierarquia de Classes de Dispositivos: Classe abstrata de dispositivo, classes de controle, dispositivos controlados e implementações concretas
  • Gerenciamento de Componentes de Circuito: Circuitos série e paralelo usando padrão composite

1.3.2 Aplicação de Padrões de Projeto

Padrão Factory

A interface DeviceFactory define o método createDevice(String id, String number). A classe ConcreteDeviceFactory implementa esta interface, criando instâncias concretas baseadas no primeiro caractere do ID do dispositivo.

Vantagens:

  • Encapsulamento: Lógica de criação concentrada na classe factory
  • Extensibilidade: Novos tipos de dispositivos只需 estender o método factory
Padrão Composite

A interface CircuitComponent define operações básicas como cálculo de tensão, obtenção de estado e resistência. As classes SeriesCircuit e ParallelCircuit implementam esta interface, permitindo contenção recursiva de outros componentes.

1.3.3 Hierarquia de Classes

Hierarquia de Dispositivos
  • Device (classe abstrata): Implementa CircuitComponent, contém atributos e métodos básicos como ID, número, resistência e tensão
  • ControlDevice (classe abstrata): Herda de Device, adiciona método abstrato toggle()
  • ControlledDevice (classe abstrata): Herda de Device, classe base para dispositivos controlados

Dispositivos Concretos:

  • SwitchDevice: Interruptor, implementa método toggle()
  • MutualSwitch: Interruptor mutuamente exclusivo
  • GearAdjuster: Controlador de velocidade por etapas
  • ContinuousAdjuster: Controlador de velocidade contínuo
  • IncandescentLight: Lâmpada incandescente
  • FluorescentLight: Lâmpada fluorescente
  • CeilingFanDevice: Ventilador de teto
  • FloorFanDevice: Ventilador de chão
  • CurtainDevice: Cortina automatizada
Hierarquia de Componentes de Circuito
  • SeriesCircuit: Calcula resistência total e distribuição de tensão
  • ParallelCircuit: Calcula resistência equivalente e distribuição de tensão
  • CurtainCircuit: Gerencia dispositivo de cortina e brilho total
Classes de Gerenciamento
  • CircuitManagerRefactored: Gerencia todos os dispositivos e componentes de circuito
  • InputHandlerRefactored: Processa dados de entrada
  • Main: Ponto de entrada do programa

1.3.4 Processamento e Parsing de Entrada

Formato de Entrada

A entrada consiste em múltiplas linhas de instruções até "end". As instruções são divididas em:

  • Instruções de Conexão: Começam com #T ou #M (circuito série T ou paralelo M)
  • Instruções de Comando: Começam com # seguido do identificador do dispositivo (#K, #H, #F, #L, etc.)
Lógica de Parsing

Parsing de Conexão:

  • Uso de expressões regulares para extrair ID do circuito e conexões internas
  • Criação de instâncias de SeriesCircuit para #T
  • Criação de instâncias de ParallelCircuit para #M

Parsing de Comando:

  • Identificação do tipo de dispositivo e operação pelo prefixo
  • Exemplos: #K1 = alternar interruptor K1, #F2+ = aumentar velocidade F2, #L3:0.75 = definir razão de L3 como 0.75

1.3.5 Construção do Circuito e Propagação de Tensão

Construção do Circuito
  • Criação de dispositivos via ConcreteDeviceFactory
  • Componentes de circuito criados via parsing de instruções de conexão
Propagação de Tensão
  • Tensão de entrada assumida como 220V (VCC)
  • Chamada de computeVoltage(220.0) no circuito total Cálculo recursivo de distribuição de tensão para cada componente

Cálculo de Brilho Total:

  • Itera por todos os dispositivos de iluminação
  • Ajusta porcentaje de abertura da cortina conforme brilho total

1.3.6 Execução de Comandos

  • Controle de Interruptor: Métodos toggleSwitch e toggleMutualSwitch
  • Controle de Velocidade: adjustGear para controladores por etapas, setContinuousGear para controladores contínuos

1.3.7 Exibição de Estado

  • Exibição inicial: Método displayInitialStatus exclui interruptores
  • Exibição final: Método displayStatus mostra todos os dispositivos

1.3.8 Tratamento de Erros

  • Exceções: IllegalArgumentException para tipos desconhecidos, circuitos indefinidos, formatos inválidos
  • Validação de entrada: Verificações necessárias ao definir proporções e adicionar componentes
  1. Arquitetura do Sistema - Versão 4

2.1 Métricas do Código

  • Arquivo Principal: PTA8.java
  • Linhas de Código: 1.737 linhas
  • Total de Declarações: 986 declarações
  • Porcentagem de Declarações de Ramificação: 24,4%
  • Classes e Interfaces: 43 classes
  • Métodos por Classe: média de 9,12 métodos por classe
  • Declarações por Método: média de 10,04 declarações por método
  • Complexidade Máxima: 1
  • Profundidade Máxima de Bloco: 8

2.2 Estrutura de Classes e Componentes Principais

2.2.1 Hierarquia de Classes Base de Circuito

CircuitBase (classe abstrata)

Atributos:

  • label: Rótulo do dispositivo
  • deviceID: Número do dispositivo
  • inVoltage: Tensão de entrada
  • outVoltage: Tensão de saída
  • resistance: Resistência
  • currentFlow: Corrente
  • pinHigh: Estado do pino

Métodos Abstratos:

  • updateStatus(double voltIn, double voltOut)
  • updateStatus(double voltIn, double voltOut, double amp)
  • getStateInfo()

CommanderUnit: Herda de CircuitBase, para dispositivos de controle

FollowerUnit: Herda de CircuitBase, para dispositivos passivos, adiciona atributo de resistência

2.2.2 Componentes Principais

PowerCluster

Responsabilidades:

  • Gerenciar todos os dispositivos e configurações de circuito
  • Contém mapa de dispositivos (moduleMap), configurações série (seriesMap) e paralelo (parallelMap)
  • Processar comandos de controle e atualizar estados do circuito

CircuitCalculator

Responsabilidades:

  • Calcular resistência equivalente de circuitos série e paralelo
  • Calcular brilho total de iluminação

CircuitStateChecker

Responsabilidades:

  • Verificar estado de circuitos série e paralelo
  • Retornar códigos de estado: -1 (aberto), 1 (curto-circuito), 0 (normal)

CircuitIlluminationCalculator

Calcula o brilho total de todos os dispositivos de iluminação no circuito.

CircuitRefresher e CircuitRefreshHelper

Atualizam estados de todos os dispositivos, incluindo tensão e corrente. Processam lógica especial para dispositivos como chaves multipath e atualizam estados de cortinas inteligentes.

CircuitController

Analisa e processa comandos de controle, atualiza estados de dispositivos correspondentes.

InputAnalyzer

Lê e analisa instruções de entrada, configura relações série e paralelo do circuito. Usa padrão factory IDeviceCreator para criar instâncias de dispositivos.

DataOutflow

Gera e outputting informações de estado de todos os dispositivos no circuito, ordenando e formatando a saída.

2.2.3 Fluxo de Simulação de Circuito

1. Análise de Entrada:

  • InputAnalyzer lê comandos de entrada
  • Analisa configurações de circuito série (começa com #T) e paralelo (começa com #M)
  • Usa padrão factory para criar instâncias de dispositivos e adicionar a PowerCluster

2. Processamento de Comandos de Controle:

  • Comandos começando com # são analisados por CircuitController
  • Atualiza estados de dispositivos correspondentes

3. Cálculo e Atualização do Circuito:

  • CircuitCalculator calcula resistência equivalente e verifica estado do circuito
  • CircuitRefresher atualiza tensão e corrente de cada dispositivo conforme resultados

4. Saída de Estado:

  • DataOutflow gera e outputting informações de estado atual de todos os dispositivos

Desafios e Soluções

  1. Versão 3 - Problemas e Soluções

1.1 Flexibilidade Insuficiente na Criação de Dispositivos

Problema: Uso inicial de instruções switch simples baseadas no primeiro caractere do ID do dispositivo. À medida que os tipos de dispositivos aumentavam, o código se tornava difícil de manter.

Solução: Implementação do padrão factory, concentrando a lógica de criação em DeviceFactory e ConcreteDeviceFactory. Novos tipos de dispositivos只需 adicionar casos correspondentes.

interface DeviceFactory {
    Device criarDispositivo(String id, String numero);
}

class ConcreteDeviceFactory implements DeviceFactory {
    @Override
    public Device criarDispositivo(String id, String numero) {
        char tipo = id.charAt(0);
        switch (tipo) {
            case 'K':
                return new InterruptorSimples(id, numero);
            case 'F':
                return new ControladorVelocidade(id, numero);
            // outros casos...
            default:
                throw new IllegalArgumentException("Tipo desconhecido: " + tipo);
        }
    }
}

1.2 Complexidade no Parsing de Entrada

Problema: Métodos iniciais de parsing usavam divisão de string simples, resultando em código redundante e difícil de manter.

Solução: Uso de expressões regulares para correspondência precisa e extração de informações importantes das instruções.

private void processarCircuitoSerie(String linha) {
    Padrao padrao = Padrao.compile("#T(\\d+):\\[(.+)\\]");
    Combinador combinador = padrao.matcher(linha);
    if (combinador.encontrou()) {
        // processar e construir circuito série
    } else {
        throw new IllegalArgumentException("Formato inválido: " + linha);
    }
}

1.3 Precisão na Lógica de Propagação de Tensão

Problema: O design inicial não considerava adequadamente os estados reais dos dispositivos, causando cálculos de tensão imprecisos.

Solução: No método computeVoltage de cada dispositivo, ajuste dinâmico de resistência e tensão de saída conforme o estado do dispositivo.

class InterruptorSimples extends DispositivoControle {
    private boolean estadoAtivo;

    @Override
    public void calcularTensao(double tensaoEntrada) {
        this.tensaoEntrada = tensaoEntrada;
        if (estadoAtivo) {
            this.tensaoSaida = tensaoEntrada;
            this.resistencia = 0.0;
        } else {
            this.tensaoSaida = 0;
            this.resistencia = Double.MAX_VALUE;
        }
    }
}

1.4 Tratamento Especial para Dispositivos de Cortina

Problema: Cortinas requerem ajuste dinâmico de porcentaje de abertura conforme brilho total, criando complexidade adicional.

Solução: Criação de classe CurtainCircuit especializada para gerenciar o relacionamento entre dispositivo de cortina e brilho total.

class CurtainCircuit implements ComponenteCircuito {
    private DispositivoCortina cortina;
    private double brilhoTotal;

    @Override
    public void calcularTensao(double tensaoEntrada) {
        cortina.definirBrilhoTotal(brilhoTotal);
        cortina.calcularTensao(tensaoEntrada);
    }
}

1.5 Condição de Término no Processamento de Entrada

Problema: Possibilidade de loop infinito ou terminação anormal se "end" estiver faltando.

Solução: Reforço da robustez no processamento de entrada para garantir terminação segura.

public List<string> lerEntrada() {
    while (scanner.temProximaLinha()) {
        String linha = scanner.proximaLinha().trim();
        if (linha.equalsIgnoreCase("end")) {
            break;
        }
        linhasEntrada.add(linha);
    }
    return linhasEntrada;
}</string>
  1. Versão 4 - Problemas e Soluções

2.1 Complexidade no Gerenciamento de Estado do Circuito

Problema: Múltiplos estados (aberto, curto-circuito, normal). A detecção precisa e tratamento desses estados era desafiadora, causando confusão lógica e recursão infinita.

Solução: Redesign da lógica de verificação de estado usando modelo de máquina de estados mais claro. Introdução de mecanismo de cache para evitar cálculos redundantes.

2.2 Tratamento de Chaves Multipath e Circuitos Paralelos

Problema: Lógica complexa para MultiPathSwitch e circuitos paralelos, gerenciamento de múltiplos pinos e caminhos.

Solução: Aumento de testes unitários e simulação de diferentes configurações. Otimização de estruturas de dados para gerenciamento de caminhos.

2.3 Flexibilidade no Parsing de Entrada e Criação de Dispositivos

Problema: Lógica de parsing rígida que não conseguia lidar com formatos de entrada complexos.

Solução: Introdução de framework de parsing mais robusto usando expressão regular e máquina de estados. Otimização do padrão factory para criação dinâmica.

2.4 Problemas de Desempenho com Cálculos Recursivos

Problema: Uso extensivo de recursão em cálculos de resistência série e paralela, causando problemas de desempenho com circuitos grandes.

Solução: Conversão de algoritmos recursivos para iterativos. Otimização usando programação dinâmica e cache.

Sugestões de Melhoria

  1. Versão 3 - Extensão e Otimização de Padrões

1.1 Extensão do Padrão Factory

Análise Atual: O uso de instruções switch no ConcreteDeviceFactory torna o código difícil de manter com novos dispositivos.

Sugestão: Implementar padrão Registry Factory, eliminando hardcoding do switch.

interface CriadorDispositivo {
    Dispositivo criar(String id, String numero);
}

class FabricaDispositivos implements FabricaDispositivosIF {
    private Map<Character, CriadorDispositivo> registro = new HashMap<>();

    public FabricaDispositivos() {
        registrarDispositivo('K', InterruptorSimples::new);
        registrarDispositivo('F', ControladorVelocidade::new);
        registrarDispositivo('L', ControladorContinuo::new);
    }

    public void registrarDispositivo(char tipo, CriadorDispositivo criador) {
        registro.put(tipo, criador);
    }

    @Override
    public Dispositivo criarDispositivo(String id, String numero) {
        char tipo = id.charAt(0);
        CriadorDispositivo criador = registro.get(tipo);
        if (criador != null) {
            return criador.criar(id, numero);
        }
        throw new IllegalArgumentException("Tipo desconhecido: " + tipo);
    }
}

1.2 Aplicação do Padrão Estratégia para Cálculo de Tensão

Análise Atual: Cada dispositivo tem lógica diferente em computeVoltage, aumentando a complexidade.

Sugestão: Separar lógica de cálculo de tensão usando padrão Strategy.

interface EstrategiaCalculoTensao {
    double calcularTensaoSaida(Dispositivo dispositivo, double tensaoEntrada);
}

class EstrategiaInterruptor implements EstrategiaCalculoTensao {
    @Override
    public double calcularTensaoSaida(Dispositivo dispositivo, double tensaoEntrada) {
        InterruptorSimples interruptor = (InterruptorSimples) dispositivo;
        return interruptor.estaAtivo() ? tensaoEntrada : 0.0;
    }
}

class EstrategiaControladorVelocidade implements EstrategiaCalculoTensao {
    @Override
    public double calcularTensaoSaida(Dispositivo dispositivo, double tensaoEntrada) {
        ControladorVelocidade controlador = (ControladorVelocidade) dispositivo;
        return tensaoEntrada * controlador.getNivelAtual();
    }
}
  1. Versão 4 - Melhorias de Princípios de Design

2.1 Violação do Princípio da Responsabilidade Única

Problema: Múltiplas classes assumem muitas responsabilidades, aumentando a complexidade.

Exemplo:

class CalculadoraCircuito {
    private final ClusterEnergia cluster;
    private final VerificadorEstadoCircuito verificador;
    private final CalculadorIluminacao calculadorIluminacao;

    // Múltiplos métodos para diferentes responsabilidades
    public double calcularResistenciaSerie(String idRota) { ... }
    public double calcularResistenciaParalelo(String idRota) { ... }
    public double iluminacaoTotal() { ... }
}

Sugestão: Dividir responsabilidades em classes separadas:

interface CalculadorResistencia {
    double calcularResistenciaSerie(String idRota);
    double calcularResistenciaParalelo(String idRota);
}

class CalculadorResistenciaImpl implements CalculadorResistencia {
    private final ClusterEnergia cluster;
    private final VerificadorEstadoCircuito verificador;
    // Implementação otimizada
}

class AtualizadorCortina {
    private final ClusterEnergia cluster;
    private final CalculadorResistencia calculador;

    public void atualizarCortinas() {
        double valorIluminacao = calculador.calcularIluminacaoTotal();
        for (DispositivoBase dev : cluster.moduleMap.values()) {
            if (dev instanceof CortinaInteligente) {
                CortinaInteligente cortina = (CortinaInteligente) dev;
                cortina.definirNivelLuz(valorIluminacao);
            }
        }
    }
}

2.2 Uso de Strings para Identificação de Dispositivos

Problema: Uso de strings para tipos de dispositivos e identificadores de rota causa erros e dificuldades de manutenção.

Sugestão: Usar enumerações para tipo seguro:

enum TipoDispositivo {
    INTERRUPTOR("K"),
    DIAL_ETAPA("F"),
    DIAL_SUAVE("L"),
    LAMPADA_QUENTE("B"),
    LAMPADA_BRANCA("R"),
    VENTILADOR_TETO("D"),
    VENTILADOR_SOLO("A"),
    CHAVE_MULTIPATH("H"),
    CORTINA_INTELIGENTE("S"),
    TUBO_UNIDIRECIONAL("P");

    private final String codigo;

    TipoDispositivo(String codigo) {
        this.codigo = codigo;
    }

    public String getCodigo() {
        return codigo;
    }

    public static TipoDispositivo fromCodigo(String codigo) {
        for (TipoDispositivo tipo : values()) {
            if (tipo.getCodigo().equalsIgnoreCase(codigo)) {
                return tipo;
            }
        }
        throw new IllegalArgumentException("Tipo desconhecido: " + codigo);
    }
}

Vantagens das Enumerações:

  • Segurança de tipo: Reduz erros de digitação
  • Manutenção: Gerenciamento centralizado de tipos
  • Legibilidade: Nomes descritivos
  • Verificação em tempo de compilação

Conclusão

Estes projetos de programação sobre simulação de circuitos elétricos residenciais representam desafios técnicos significativos. O primeiro projeto focou na construção de uma base sólida com padrões de projeto como Factory e Composite, enquanto o segundo projeto expandiu o sistema para lidar com complexidade adicional incluindo verificação de estado de curto-circuito, cálculos de corrente, e tratamento de dispositivos especiais como chaves multipath e cortinas inteligentes.

A experiência prática nestes projetos permitiu desenvolver habilidades em:

  • Design de sistemas orientados a objetos com padrões de projeto adequados
  • Processamento de entrada complexa e parsing de formatos estruturados
  • Cálculos de circuitos elétricos e propagação de parâmetros
  • Gerenciamento de múltiplos estados e tratamento de erros
  • Otimização de código para desempenho e manutenibilidade

As lições aprendidas destacam a importância de aplicar princípios sólidos de design de software desde o início do projeto, planejar a arquitetura considerando a escalabilidade futura, e utilizar estruturas de dados apropriadas para o domínio do problema.

Tags: java circuit-simulation design-patterns electrical-engineering object-oriented-programming

Publicado em 6-21 03:07