Best Practices und Clean Code in Java
Sauberen Code zu schreiben significa creare software leggibile, manutenibile e professionale. I principi SOLID e le best practices guidano verso un design robusto e scalabile.
Was du lernen wirst
- SOLID-Prinzipien
- Naming conventions
- Code smell e refactoring
- Gestione errori corretta
- Documentazione efficace
SOLID-Prinzipien
// ERRATO: classe con troppe responsabilità
class StudenteManager {
void salvaStudente(Studente s) { /* salva su DB */ }
void inviaEmail(Studente s) { /* invia email */ }
String generaReport(Studente s) { /* genera report */ }
void validaStudente(Studente s) { /* valida dati */ }
}
// CORRETTO: una responsabilità per classe
class StudenteRepository {
void salva(Studente s) { /* salva su DB */ }
Studente trova(Long id) { /* cerca su DB */ }
}
class EmailService {
void invia(String to, String messaggio) { /* invia */ }
}
class StudenteReportGenerator {
String genera(Studente s) { /* genera report */ }
}
class StudenteValidator {
boolean isValido(Studente s) { /* valida */ }
}
// ERRATO: modifica necessaria per nuovi tipi
class CalcolatorePagamento {
double calcola(String tipo, double importo) {
if (tipo.equals("CARTA")) {
return importo * 0.98; // 2% fee
} else if (tipo.equals("PAYPAL")) {
return importo * 0.97; // 3% fee
}
// Aggiungere nuovo else-if per ogni tipo...
return importo;
}
}
// CORRETTO: aperto per estensione, chiuso per modifica
interface MetodoPagamento {
double calcolaImportoFinale(double importo);
}
class PagamentoCarta implements MetodoPagamento {
public double calcolaImportoFinale(double importo) {
return importo * 0.98;
}
}
class PagamentoPayPal implements MetodoPagamento {
public double calcolaImportoFinale(double importo) {
return importo * 0.97;
}
}
// Nuovo metodo: basta aggiungere classe
class PagamentoCrypto implements MetodoPagamento {
public double calcolaImportoFinale(double importo) {
return importo * 0.99;
}
}
// ERRATO: sottoclasse che viola il contratto
class Rettangolo {
protected int larghezza;
protected int altezza;
public void setLarghezza(int l) { larghezza = l; }
public void setAltezza(int a) { altezza = a; }
public int getArea() { return larghezza * altezza; }
}
class Quadrato extends Rettangolo {
public void setLarghezza(int l) {
larghezza = l;
altezza = l; // Viola il comportamento atteso!
}
}
// CORRETTO: composizione invece di ereditarietà problematica
interface Forma {
int getArea();
}
class RettangoloCorretto implements Forma {
private final int larghezza;
private final int altezza;
RettangoloCorretto(int larghezza, int altezza) {
this.larghezza = larghezza;
this.altezza = altezza;
}
public int getArea() { return larghezza * altezza; }
}
class QuadratoCorretto implements Forma {
private final int lato;
QuadratoCorretto(int lato) {
this.lato = lato;
}
public int getArea() { return lato * lato; }
}
// ERRATO: interfaccia troppo grande
interface Worker {
void lavora();
void mangia();
void dorme();
}
class Robot implements Worker {
public void lavora() { /* ok */ }
public void mangia() { /* non applicabile! */ }
public void dorme() { /* non applicabile! */ }
}
// CORRETTO: interfacce segregate
interface Lavoratore {
void lavora();
}
interface EssereVivente {
void mangia();
void dorme();
}
class Umano implements Lavoratore, EssereVivente {
public void lavora() { /* implementa */ }
public void mangia() { /* implementa */ }
public void dorme() { /* implementa */ }
}
class RobotCorretto implements Lavoratore {
public void lavora() { /* implementa solo ciò che serve */ }
}
// ERRATO: dipendenza da implementazione concreta
class NotificaService {
private EmailSender emailSender = new EmailSender();
void notifica(String messaggio) {
emailSender.invia(messaggio); // Accoppiamento stretto!
}
}
// CORRETTO: dipende da astrazione
interface NotificaSender {
void invia(String messaggio);
}
class EmailSenderImpl implements NotificaSender {
public void invia(String messaggio) { /* email */ }
}
class SmsSender implements NotificaSender {
public void invia(String messaggio) { /* sms */ }
}
class NotificaServiceCorretto {
private final NotificaSender sender;
// Dependency Injection
NotificaServiceCorretto(NotificaSender sender) {
this.sender = sender;
}
void notifica(String messaggio) {
sender.invia(messaggio);
}
}
Namenskonventionen
// ERRATO: nomi poco chiari
int d; // cosa rappresenta?
String s;
List<String> list;
void proc(int x);
// CORRETTO: nomi descrittivi
int giorniDallaScadenza;
String nomeStudente;
List<String> emailsAttivi;
void calcolaMediaVoti(int studenteId);
// Convenzioni Java
class NomeClasse {} // PascalCase
interface Calcolabile {} // PascalCase
void nomeMetodo() {} // camelCase
int variabileLocale; // camelCase
static final int COSTANTE = 10; // UPPER_SNAKE_CASE
// Booleani: prefisso is/has/can
boolean isAttivo;
boolean hasPermesso;
boolean canModificare;
// Collezioni: plurale
List<Studente> studenti;
Map<String, Integer> votiPerMateria;
// Metodi: verbo + nome
void salvaStudente(Studente s);
Studente trovaStudenteById(Long id);
boolean isStudenteAttivo(Studente s);
List<Studente> getStudentiAttivi();
Code Smells und Refactoring
// CODE SMELL: Metodo troppo lungo
void processaOrdine(Ordine o) {
// 50+ righe di codice...
}
// REFACTORING: Estrai metodi
void processaOrdine(Ordine o) {
validaOrdine(o);
calcolaTotale(o);
applicaSconti(o);
salvaOrdine(o);
inviaConferma(o);
}
// CODE SMELL: Magic numbers
if (eta > 18 && punteggio > 75) {
// ...
}
// REFACTORING: Costanti con nome
private static final int ETA_MINIMA = 18;
private static final int PUNTEGGIO_SUFFICIENTE = 75;
if (eta > ETA_MINIMA && punteggio > PUNTEGGIO_SUFFICIENTE) {
// ...
}
// CODE SMELL: Parametri booleani
void creaUtente(String nome, boolean admin, boolean attivo);
// REFACTORING: Builder o oggetti
class ConfigurazioneUtente {
private boolean admin;
private boolean attivo;
// builder pattern...
}
// CODE SMELL: Commenti che spiegano codice complesso
// Calcola la media pesata considerando i crediti
double r = 0;
int t = 0;
for (int i = 0; i < v.length; i++) {
r += v[i] * c[i];
t += c[i];
}
return r / t;
// REFACTORING: Codice auto-documentante
double calcolaMediaPesata(int[] voti, int[] crediti) {
double sommaPesata = 0;
int totaleCrediti = 0;
for (int i = 0; i < voti.length; i++) {
sommaPesata += voti[i] * crediti[i];
totaleCrediti += crediti[i];
}
return sommaPesata / totaleCrediti;
}
Fehlerbehandlung
// ERRATO: catch generico
try {
// operazione
} catch (Exception e) {
// gestione troppo generica
}
// CORRETTO: catch specifici
try {
// operazione
} catch (FileNotFoundException e) {
// gestisci file non trovato
} catch (IOException e) {
// gestisci altri errori I/O
}
// ERRATO: eccezione ignorata
try {
riskyOperation();
} catch (Exception e) {
// silenzioso - MAI fare così!
}
// CORRETTO: almeno logga
try {
riskyOperation();
} catch (Exception e) {
logger.error("Errore durante operazione", e);
throw new ServiceException("Operazione fallita", e);
}
// Eccezioni personalizzate significative
class StudenteNonTrovatoException extends RuntimeException {
private final Long studenteId;
StudenteNonTrovatoException(Long id) {
super("Studente non trovato con ID: " + id);
this.studenteId = id;
}
public Long getStudenteId() {
return studenteId;
}
}
// Usa Optional invece di null
Optional<Studente> trovaStudente(Long id);
// Chiamante
Studente s = trovaStudente(id)
.orElseThrow(() -> new StudenteNonTrovatoException(id));
Allgemeine Best Practices
Goldene Regeln
- DRY: Don't Repeat Yourself
- KISS: Keep It Simple, Stupid
- YAGNI: You Aren't Gonna Need It
- Fail Fast: Valida input all'inizio
- Immutabilità: Preferisci oggetti immutabili
- Composizione > Ereditarietà: Più flessibile
- Programma su interfacce: Non implementazioni
- Single Level of Abstraction: Un livello per metodo
// Classe ben strutturata
public class StudenteService {
private final StudenteRepository repository;
private final EmailService emailService;
private final StudenteValidator validator;
public StudenteService(StudenteRepository repository,
EmailService emailService,
StudenteValidator validator) {
this.repository = Objects.requireNonNull(repository);
this.emailService = Objects.requireNonNull(emailService);
this.validator = Objects.requireNonNull(validator);
}
public Studente registra(RegistrazioneRequest request) {
validator.valida(request);
Studente studente = creaStudente(request);
Studente salvato = repository.salva(studente);
inviaEmailBenvenuto(salvato);
return salvato;
}
private Studente creaStudente(RegistrazioneRequest request) {
return Studente.builder()
.nome(request.getNome())
.email(request.getEmail())
.dataRegistrazione(LocalDate.now())
.build();
}
private void inviaEmailBenvenuto(Studente studente) {
emailService.invia(
studente.getEmail(),
"Benvenuto " + studente.getNome()
);
}
}







