Dúvida sobre escopo de método

Bom dia!

Existe alguma diferença entre eu criar a classe acima do método como variável ou instanciando ela no método?

Exemplo:

acima do método:
private Empresa empresa;
ai com isso, dentro do método: empresa = empresaRepository.getOne(empresaId);

dentro do método:
Empressa empresa = empresaRepository.getOne(empresaId);

Sim, tem uma grande diferença, ainda mais se usado em frameworks como Spring, onde os objetos são criados apenas uma vez.

Quando vc cria fora do método, em um sistema onde há vários usuários acessando, há uma grande chance do valor da variável ser modificada por outra thread (nesse exemplo, vamos assumir que a instância da classe de serviço, por exemplo, é a mesma).

Agora se a classe onde está esse método for instanciado toda vez para cada execução, não significa que irá dá problema, mas de qualquer forma, não é uma boa prática.

Mesmo que vc queira declarar a variável na classe, teria que entender a ração de se fazer isso. Vamos supor que seja para reutilizar em outro método, em vez disso, é melhor passar o valor como parâmetro para o método.

1 curtida

No meu caso, tenho 1 método geral, que faz todos os cálculos necessários, e tenho outro método, que é um loop passando por todos os autônomos da empresa para gerar, afinal o cálculo é o mesmo.

Para não ter que bater no banco a cada volta do loop, pensei:

Neste caso específico o id da empresa sempre será o mesmo, então criei como variável acima do método, e fiz uma lógica para passar ali apenas 1 vez.

E também, algumas variáveis tem que ser utilizadas em condições IF, gerando erro dependendo da onde estivesse no código.

Todo este “trabalho” é feito no Service.

Um exemplo do que quero evitar seria:

idTabelaIRRFCalc e irrfCalc foram criadas fora do método. um sendo long, e outro sendo a classe.

    if (passarAqui) {
       idTabelaIRRFCalc = irrfRepository.findIRRF(mesAno);
       irrfCalc = irrfRepository.getOne(idTabelaIRRFCalc);
}

montei uma lógica para passar ai uma vez só, caso fosse o método do loop, que cairia no método principal, assim evitando bater no BD a cada volta, para pegar o mesmo registro.

Tem outra abordagem que vc pode seguir para contornar isso. Vc pode usar algum mecanismo de cache na consulta do banco ou aplicar memoization para otimizar o seu código. Dependendo do framework que vc estiver usando talvez já tenha algo para usar, mas vc também pode criar o seu próprio.

1 curtida

no caso estou utilizando Spring. vou tentar passar como parâmetro no método para ver como fica.

e também pesquisar mais sobre esses tópicos que citou.

e no caso de eu já ter instanciado a classe, e não poder instanciar ela de novo, pois irei perder seus valores?

mesmo assim, não seria correto aplicar a criação da variável fora do método?

a variável estando em condições IF, se não estiver fora do método, não aceita em algumas partes do código.

É possível vc mandar o código desse service relacionado com essa parte que tem o loop, pra gente ver?

1 curtida

Eu removi algumas partes do código, se não ficaria mt grande e confuso para a questão em si. Estas são as variáveis que estão dentro da classe, e acima do método. Algumas podem não estar no código, devido ao q eu removi, mas todas são utilizadas.

@Autowired
private PagamentoAutonomoRepository pagamentoAutonomoRepository;
@Autowired
private AutonomoRepository autonomoRepository;
@Autowired
private EmpresaRepository empresaRepository;
@Autowired
private ContribuicaoInssRepository contribuicaoInssRepository;
@Autowired
private IrrfRepository irrfRepository;
@Autowired
private InssEnquadramentoRepository inssEnquadramentoRepository;
@Autowired
private ValoresRelatoriosRepository valoresRelatoriosRepository;
@Autowired
private ManutencaoRepository fiscalManutencaoRepository;
private boolean passarAqui = true;
private boolean todasEmpresas = false;
private Empresa empresa;
private InssEnquadramento inssEnquadramento;
private Manutencao fiscalManutencao;
private Irrf irrfCalc;
private ContribuicaoInss contribuicaoInss;
private Double valorValoresRelatorio;
private Double valorPrimeiroRegistro;

public void insert(PagamentoAutonomoDTO dto) {
	final Long idTabelaIRRFCalc;
	final Long idFiscalManutencao;
	final Long idContribuicaoInss;
	
	// Se cair no insertTodos, não tem necessidade de passar aqui toda vez
	if (passarAqui) {
		valorValoresRelatorio = valoresRelatoriosRepository.findByCodigoMesAno("001", dto.getMesAno());
		valorPrimeiroRegistro = valoresRelatoriosRepository.findByPrimeiroRegistro();	
		
		idTabelaIRRFCalc = irrfRepository.findIRRF(mesAno);
		irrfCalc = irrfRepository.getOne(idTabelaIRRFCalc);
		
		empresa = empresaRepository.getOne(dto.getEmpresaId());
		inssEnquadramento = inssEnquadramentoRepository.findByEmpresaId(dto.getEmpresaId());
		
		idFiscalManutencao = fiscalManutencaoRepository.findIdByEmpresaId(dto.getEmpresaId());
		fiscalManutencao = fiscalManutencaoRepository.getOne(idFiscalManutencao);
		
		idContribuicaoInss = contribuicaoInssRepository.findByMesAnoContribuicaoInss(dto.getMesAno());
		contribuicaoInss = contribuicaoInssRepository.getOne(idContribuicaoInss);
	}

	Long idAutonomo = pagamentoAutonomoRepository.findAutonomoCpf(dto.getCpf());
	Autonomo autonomo = autonomoRepository.getOne(idAutonomo);
	
	if (todasEmpresas) {
		Long idEmpresa = dto.getEmpresaId();
		empresa = empresaRepository.getOne(idEmpresa);
	}
	
	var entity = new PagamentoAutonomo();
	BeanUtils.copyProperties(dto, entity);
	entity.setEmpresa(empresa);
	entity.setAutonomo(autonomo);
	
	Double valorReceber = 0.00;
	Double valorReceberContrib = 0.00;
	if (valorValoresRelatorio != null) {
		// faz algo
	} 
	else if (valorPrimeiroRegistro != null) {
		// faz algo
	}
	else {
		valorReceber = 0.00;
	}		

	
	// cálculos necessários ...

	LocalDate dataPagamento = LocalDate.parse(mesAno, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
	boolean salvar; // Ex: 20/01/2023 < 01/01/2023 - false
	if (autonomo.getDesligamento() != null && !autonomo.getDesligamento().isBefore(dataPagamento)) {
		salvar = true;
	}
	else if (autonomo.getDesligamento() == null) {
		salvar = true;
	}
	else {
		salvar = false;
	}
	
	if (salvar) {
		pagamentoAutonomoRepository.save(entity);
	}
}


public void insertTodos(PagamentoAutonomoDTO dto) {
	List<String> listaCpfAutonomoPorEmpresa = pagamentoAutonomoRepository.findCpfAutonomoByEmpresaId(dto.getEmpresaId());
	for (String cpf : listaCpfAutonomoPorEmpresa) {			
		dto.setCpf(cpf);
		insert(dto);
		if (passarAqui) {
			passarAqui = false;
		}
	}
	// Terminando o loop recebe true de novo, pois sempre deve iniciar com true
	passarAqui = true;
}


public void insertTodasEmpresas(PagamentoAutonomoDTO dto) {
	todasEmpresas = true;
	
	List<Long> empresas = empresaRepository.findEmpresas();
	for (Long idEmpresa : empresas) {
		List<String> autonomos = pagamentoAutonomoRepository.findCpfAutonomoByEmpresaId(idEmpresa);
		for (String cpfAutonomo : autonomos) {
			dto.setEmpresaId(idEmpresa);
			dto.setCpf(cpfAutonomo);
			insert(dto);
			if (passarAqui) {
				passarAqui = false;
			}				
		}
	}
	// Terminando o loop recebe true de novo, pois sempre deve iniciar com true
	passarAqui = true;
	todasEmpresas = false;
}

Acredito que de para entender +/- a ideia que tive

1 curtida

Já adianto que, soh pelo fato de vc estar usando spring, ter variáveis declaradas na classe já está errado, pois todos os beans que o spring cria, por padrão, são singleton. Dessa forma, há uma grande chance de ter problema envolvendo essas variáveis de classe quando vários usuários estiverem fazendo requisição no sistema, pois uma requisição de um usuário poderá alterar o valor da variável que foi setada pela requisição de um outro usuário.

1 curtida

Como venho da programação desktop, ainda não “penso” algumas coisas como esta q vc disse…

No caso, se eu utilizar as variáveis como parâmetro no método, vc acredita que teria algum problema?

E no caso das declarações @Autowired, eu acredito não ter problema em estar ali, correto?

Ou eu penso em replicar tudo do método insert, nos outros, ficaria um pouco grande, mas acredito que evitaria problemas futuros.

Em relação às flags, eu pensaria numa solução mais ou menos assim:

public void insert(PagamentoAutonomoDTO dto) {
	insert(dto, true, false);
}

private void insert(PagamentoAutonomoDTO dto, boolean carregarValores, boolean todasEmpresas) {
	final Long idTabelaIRRFCalc;
	final Long idFiscalManutencao;
	final Long idContribuicaoInss;
	
	// Se cair no insertTodos, não tem necessidade de passar aqui toda vez
	if (carregarValores) {
		valorValoresRelatorio = valoresRelatoriosRepository.findByCodigoMesAno("001", dto.getMesAno());
		valorPrimeiroRegistro = valoresRelatoriosRepository.findByPrimeiroRegistro();	
		
		idTabelaIRRFCalc = irrfRepository.findIRRF(mesAno);
		irrfCalc = irrfRepository.getOne(idTabelaIRRFCalc);
		
		empresa = empresaRepository.getOne(dto.getEmpresaId());
		inssEnquadramento = inssEnquadramentoRepository.findByEmpresaId(dto.getEmpresaId());
		
		idFiscalManutencao = fiscalManutencaoRepository.findIdByEmpresaId(dto.getEmpresaId());
		fiscalManutencao = fiscalManutencaoRepository.getOne(idFiscalManutencao);
		
		idContribuicaoInss = contribuicaoInssRepository.findByMesAnoContribuicaoInss(dto.getMesAno());
		contribuicaoInss = contribuicaoInssRepository.getOne(idContribuicaoInss);
	}

	Long idAutonomo = pagamentoAutonomoRepository.findAutonomoCpf(dto.getCpf());
	Autonomo autonomo = autonomoRepository.getOne(idAutonomo);
	
	if (todasEmpresas) {
		Long idEmpresa = dto.getEmpresaId();
		empresa = empresaRepository.getOne(idEmpresa);
	}
	
	var entity = new PagamentoAutonomo();
	BeanUtils.copyProperties(dto, entity);
	entity.setEmpresa(empresa);
	entity.setAutonomo(autonomo);
	
	Double valorReceber = 0.00;
	Double valorReceberContrib = 0.00;

	if (valorValoresRelatorio != null) {
		// faz algo
	} else if (valorPrimeiroRegistro != null) {
		// faz algo
	} else {
		valorReceber = 0.00;
	}		

	// cálculos necessários ...

	LocalDate dataPagamento = LocalDate.parse(mesAno, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
	boolean salvar; // Ex: 20/01/2023 < 01/01/2023 - false
	
	if (autonomo.getDesligamento() != null && !autonomo.getDesligamento().isBefore(dataPagamento)) {
		salvar = true;
	} else if (autonomo.getDesligamento() == null) {
		salvar = true;
	} else {
		salvar = false;
	}
	
	if (salvar) {
		pagamentoAutonomoRepository.save(entity);
	}
}
public void insertTodos(PagamentoAutonomoDTO dto) {
	List<String> listaCpfAutonomoPorEmpresa = pagamentoAutonomoRepository.findCpfAutonomoByEmpresaId(dto.getEmpresaId());
	
	for (String cpf : listaCpfAutonomoPorEmpresa) {			
		dto.setCpf(cpf);
		insert(dto, false, false);
	}
}
public void insertTodasEmpresas(PagamentoAutonomoDTO dto) {
	List<Long> empresas = empresaRepository.findEmpresas();
	
	for (Long idEmpresa : empresas) {
		List<String> autonomos = pagamentoAutonomoRepository.findCpfAutonomoByEmpresaId(idEmpresa);
		
		for (String cpfAutonomo : autonomos) {
			dto.setEmpresaId(idEmpresa);
			dto.setCpf(cpfAutonomo);
			insert(dto, false, true);
		}
	}
}

Teria um método privado que receberia flags para fazer o controle do processamento, e manteria o resto como estava e passaria os booleans de acordo com o que for necessário. Isso seria para responder sua dúvida inicial.

Agora o bacana mesmo seria uma refatoração no método de inserir para, talvez, quebrar esse método em outros para deixar a responsabilidade mais definida, fazendo que a cada parte seja chamada de acordo com o que precisa ser executado.

1 curtida

Como o método insert está tudo ok, os cálculos, valores batendo, para não tomar mt tempo, acho que irei pegar td deste método, e incluir nos loops… não é o ideal, mas não posso tomar Mt tempo nisto :confused:

Em relação aos @Autowired está ok. Faz parte de como as coisas são feitas mesmo. Se fosse para mudar essa parte, seria trocando a injeção pela anotação para usar o construtor, pois ajuda na hora de criar testes. Assim:

private final PagamentoAutonomoRepository pagamentoAutonomoRepository;
private final AutonomoRepository autonomoRepository;
private final EmpresaRepository empresaRepository;
private final ContribuicaoInssRepository contribuicaoInssRepository;
private final IrrfRepository irrfRepository;
private final InssEnquadramentoRepository inssEnquadramentoRepository;
private final ValoresRelatoriosRepository valoresRelatoriosRepository;
private final ManutencaoRepository fiscalManutencaoRepository;

public NomeDaClasse(
	PagamentoAutonomoRepository pagamentoAutonomoRepository,
	AutonomoRepository autonomoRepository,
	EmpresaRepository empresaRepository,
	ContribuicaoInssRepository contribuicaoInssRepository,
	IrrfRepository irrfRepository,
	InssEnquadramentoRepository inssEnquadramentoRepository,
	ValoresRelatoriosRepository valoresRelatoriosRepository,
	ManutencaoRepository fiscalManutencaoRepository
) {
	this.pagamentoAutonomoRepository = pagamentoAutonomoRepository;
	this.autonomoRepository = autonomoRepository;
	this.empresaRepository = empresaRepository;
	this.contribuicaoInssRepository = contribuicaoInssRepository;
	this.irrfRepository = irrfRepository;
	this.inssEnquadramentoRepository = irrfRepository;
	this.valoresRelatoriosRepository = valoresRelatoriosRepository;
	this.fiscalManutencaoRepositor = fiscalManutencaoRepositor;
}
1 curtida

Do jeito que fiz, deve funcionar para o que vc precisa fazer e sem precisar duplicar código. É comum quando vc precisa fazer um ajuste sem quebrar o código. Só precisa checar se os valores dos booleans que passei no loop estão de acordo com o que precisa ser executado (talvez eu tenha trocado algo que deveria ser true e coloquei false, por exemplo).

No caso das variáveis de classe, então eu também teria problema em criar fora dos métodos, correto?

pelo q entendi, variável fora da classe, Não utilizar.

1 curtida

Isso, principalmente se for em classes gerenciadas pelo spring, que é o seu caso, por conta de serem singleton.

1 curtida

Um exemplo que não teria problema ter variáveis declaradas na classe seria onde uma classe que é instanciada no momento em que o processamento for realizado. Exemplo tosco soh para exemplificar essa ideia:

public class ProcessadorCalculoComplexo {
  private final int numA;
  private final int numB;

  public CalculadoraSimples(int numA, int numB) {
    this.numA = numA;
    this.numB = numB;
  }

  public int recuperarValorCalculado() {
    int valorCalculado = numA * numB // imagine que aqui seja calculo complexo =)
    return valorCalculado;
  }
}

E para usar:

ProcessadorCalculoComplexo calcA = new ProcessadorCalculoComplexo(10, 20);
int resultA = calcA.recuperarValorCalculado();

Nesse exemplo simples não teria problemas ter variáveis declaradas na classe, pois a cada vez que a classe é usada, uma instância nova é criada, o que evita que uma requisição de usuário, por exemplo, afete os valores setados por outra requisição de outro usuário, no caso de um sistema web onde podem haver várias requisições ocorrendo no servidor.

1 curtida

muito obrigado!! vou enxergar as coisas de outro jeito agora.

irei analisar td que me explicou e vou tentar aqui, ver qual a melhor saída.

novamente, mt obrigado!

1 curtida