Introdução
- 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.
- 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
- 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): ImplementaCircuitComponent, contém atributos e métodos básicos como ID, número, resistência e tensãoControlDevice(classe abstrata): Herda deDevice, adiciona método abstratotoggle()ControlledDevice(classe abstrata): Herda deDevice, classe base para dispositivos controlados
Dispositivos Concretos:
SwitchDevice: Interruptor, implementa métodotoggle()MutualSwitch: Interruptor mutuamente exclusivoGearAdjuster: Controlador de velocidade por etapasContinuousAdjuster: Controlador de velocidade contínuoIncandescentLight: Lâmpada incandescenteFluorescentLight: Lâmpada fluorescenteCeilingFanDevice: Ventilador de tetoFloorFanDevice: Ventilador de chãoCurtainDevice: Cortina automatizada
Hierarquia de Componentes de Circuito
SeriesCircuit: Calcula resistência total e distribuição de tensãoParallelCircuit: Calcula resistência equivalente e distribuição de tensãoCurtainCircuit: Gerencia dispositivo de cortina e brilho total
Classes de Gerenciamento
CircuitManagerRefactored: Gerencia todos os dispositivos e componentes de circuitoInputHandlerRefactored: Processa dados de entradaMain: 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
SeriesCircuitpara #T - Criação de instâncias de
ParallelCircuitpara #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
toggleSwitchetoggleMutualSwitch - Controle de Velocidade:
adjustGearpara controladores por etapas,setContinuousGearpara controladores contínuos
1.3.7 Exibição de Estado
- Exibição inicial: Método
displayInitialStatusexclui interruptores - Exibição final: Método
displayStatusmostra todos os dispositivos
1.3.8 Tratamento de Erros
- Exceções:
IllegalArgumentExceptionpara tipos desconhecidos, circuitos indefinidos, formatos inválidos - Validação de entrada: Verificações necessárias ao definir proporções e adicionar componentes
- 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
- 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>
- 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
- 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();
}
}
- 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.