Casos Usuais

1. Estado longe de Comportamento

Untitled

O encapsulamento vazou, o comportamento e estado foram separados.

// Deveria ser:
public Class AlgumLugar{
	public void metodo(){
		Aluno aluno = new Aluno();
		if(aluno.**estaInscritoEmTreinamento**()){
			// faz algo
		}
	}
}

2. Abstrair decisões tecnológicas que podem ser mudadas no futuro

public class RecebeRespostaController {
	public void executa(){
		Resposta novaResposta = // faz uma lógica para guardar uma resposta;
		//pseudocódigo
		//rabbitTemplate.send("analisaRespostas", mensagem) -- acoplado
		filaProcessamentoResposta.send(mensagem);
	}
}

Essa decisão é bem mais de negócio do que de código, o código reflete sua decisão, acoplar com algum vendor em específico não é necessariamente ruim, mas se você está incerto ou possui quase certeza que isso precisará ser trocado em breve, crie interfaces comuns à essas funcionalidades.

3. Inverter Dependências

public class Treinamento {
	public Treinamento (Collection<NovaAtividadeRequest> atividades){
		this.atividades = atividades.map (a-> a.toModel(this));
		// isso é horrível, uma classe de domínio, o básico do sistema está projetada para observar classes para receber parâmetros da web.
	}
	@Getter
	@Setter
	Collection<NovaAtividadeRequest> atividades;	
}

Quando a camada mais interna se acopla à camada mais externa, qualquer alteração nessa camada impactará todo o sistema, que tem como fundação a camada interna acoplada.

public interface NovaAtividade{
	Atividade toModel(Treinamento treinamento);
}

public class NovaAtividadeRequest implements NovaAtividade{
	toModel(Treinamento treinamento){
		//
	}
}

public class Treinamento {
	public Treinamento (Collection<**INovaAtividade**> atividades){
		this.atividades = atividades.map (a-> a.toModel(this));
	}
}

Assim, passamos a ter uma dependência de uma interface de domínio, bem mais estável.

4. Adapters / Wrappers para libs que são incertas

Se não tiver certezas quanto à libs, pode usar wrappers que utilizam dessa lib

public class AnalisaComplexidadeDeCodigo{
	public int analisa(){
		new Biblioteca biblioteca = biblioteca();
		biblioteca.analisa();
	}
}

5. Falta de uso de Construtor

Classes com campos obrigatórios e construtores públicos sem argumentos com getters/setters são úteis em casos em que a classe é usada como um recipiente de dados simples e é fácil criar a instância da classe com as informações necessárias. Essa abordagem também é útil ao trabalhar com frameworks ou bibliotecas que dependem de reflexão para criar instâncias da classe, como bibliotecas de serialização e deserialização.

Por outro lado, usar construtores com campos obrigatórios e um construtor privado sem argumentos para impor as regras do domínio é uma maneira de garantir que as instâncias da classes estejam sempre em um estado válido e pode tornar mais fácil detectar e prevenir erros em tempo de execução. Essa abordagem também é útil ao trabalhar com Design Orientado ao Domínio (DDD), onde o foco é modelar a lógica de negócios e impor as regras do domínio de problema.