CodeGym /Corsi /C# SELF /Consigli su stile e leggibilità del codice OOP

Consigli su stile e leggibilità del codice OOP

C# SELF
Livello 25 , Lezione 4
Disponibile

1. Introduzione

"Codice pulito" non è una mucca sacra, ma uno strumento reale di sopravvivenza per il programmatore. In qualsiasi progetto OOP, anche il più figo, si accumulano in fretta un sacco di classi, campi, metodi, collegamenti strani... Se rompi l’estetica e la struttura, dopo una settimana il tuo stesso codice diventa una caccia al tesoro impossibile. Se vuoi leggere di più sulla "lotta per la sopravvivenza", c’è Robert Martin, "Codice Pulito" — parla proprio di queste battaglie.

Lo stile nel codice non è "ognuno fa come gli pare", ma serve a rendere la vita più facile a tutti:

  • Un collega (o tu stesso) può capire subito cosa succede.
  • Gli errori (soprattutto architetturali) si vedono al volo.
  • Il codice è più facile da modificare, con meno errori.

Vediamo come rendere il codice OOP così bello che lo ameranno i revisori, i colleghi e pure i linters.

2. I nomi: la tua prima linea di difesa

Nominare le classi

Le classi in C# si chiamano in PascalCase (ogni parte del nome inizia con la maiuscola, tipo: MyNewClass) e il nome deve rispondere chiaramente alla domanda "Cos’è?". I nomi devono essere sostantivi!

public class StudentAccount { /* ... */ }
public class InvoiceGenerator { /* ... */ }

Male:

class doMagic { ... } // Male: Che magia? PascalCase non rispettato.

Nominare i metodi

I metodi — anche loro in PascalCase, ma qui meglio usare verbo + oggetto dell’azione:

public void PrintReport() { ... }
public string GetFormattedName() { ... }

I metodi devono riflettere un’azione (Print, Get, Save, Calculate ecc.), così leggendo capisci subito cosa succede.

Nominare campi e proprietà

I campi di solito sono private, si chiamano con la minuscola in camelCase, spesso con l’underscore davanti:

private int _count;
private Student _owner;

Le proprietàPascalCase, perché fanno parte dell’interfaccia pubblica della classe:

public int Balance { get; set; }

Variabili

Le variabili localicamelCase, il più corte e chiare possibile per il contesto:

string inputName;
int studentCount;

E per favore, variabili tipo a1, result2, something — solo se vuoi crearti una caccia al tesoro per il mese prossimo.

3. Organizzazione della struttura della classe

Mettere i membri della classe nell’ordine giusto rende la navigazione più semplice e aiuta a capire subito cosa viene dopo cosa.

Di solito si fa così:

  1. Costruttori
  2. Proprietà
  3. Metodi
  4. Tipi annidati (enum, class ecc.)

Esempio:


public class Student
{
    // --- Campi ---
    private string _name;

    // --- Costruttore ---
    public Student(string name)
    {
        _name = name;
    }

    // --- Proprietà ---
    public string Name
    {
        get => _name;
        set => _name = value;
    }

    // --- Metodi ---
    public void PrintInfo()
    {
        Console.WriteLine($"Nome: {_name}");
    }
}

Questi "blocchi" è comodo separarli con commenti (// --- Metodi ---), soprattutto nelle classi grandi. JetBrains Rider, Visual Studio e altre IDE ti fanno chiudere/aprire le sezioni al volo.

4. Commenti e documentazione

I commenti sono una cosa buona. Ma è male abusarne o scrivere "spiegazioni a codice incomprensibile" quando puoi semplicemente cambiare il codice!

Un buon commento — spiega "perché", non "cosa".

// Usiamo Guid come identificatore unico perché il sistema è distribuito
public Guid Id { get; set; }

Documentazione di metodi, classi e proprietà

Usa la documentazione xml per classi e metodi pubblici. Le IDE ti mostrano queste descrizioni quando passi sopra col mouse.


/// <summary>
/// Rappresenta uno studente universitario.
/// </summary>
public class Student
{
    /// <summary>
    /// Nome dello studente.
/// </summary>
    public string Name { get; set; }
}

Cosa NON commentare

  • Cose semplici (i++ // incrementa i di 1).
  • Variabili con nomi brutti ("// qui succede qualcosa" — sì, ma cosa?).

5. Dividi e conquista

Classi e metodi piccoli

Regola d’oro: una classe — una responsabilità (vedi Single Responsibility Principle). Se la classe Student gestisce voti, email e orari — c’è qualcosa che non va.

  • Classi fino a 300-400 righe — ok. Di più — pensaci su.
  • Metodi fino a 15-20 righe — leggibili. Eccezioni solo se è un handler di un caso grosso.

Esempio di metodo "gonfiato":


public void Process()
{
    // Notifica il cliente
    // Salva le modifiche
    // Manda l’email
    // Scrive i log
    // ... (15 passi)
}

Meglio così:


public void Process()
{
    NotifyClient();
    SaveChanges();
    SendEmail();
    LogActivity();
}

Ognuno di questi è un metodo privato separato, il codice è più compatto e facile da testare.

6. Consigli utili

Struttura visiva: formattazione, indentazione, righe vuote

Le IDE sanno formattare il codice da sole (Ctrl+K, D in Visual Studio, Ctrl+Alt+L in Rider), ma i principi devi comunque conoscerli.

  • INDENTAZIONE — 4 spazi. Niente tab, niente 2 spazi.
  • RIGHE VUOTE — separa i metodi tra loro, i campi dalle proprietà, le proprietà dai metodi.
  • GRAFFE sempre su una nuova riga per classi e metodi (stile Allman):

public class Test
{
    public void Print()
    {
        Console.WriteLine("Ciao");
    }
}

Membri "forti" e "deboli": modificatori di accesso

Cerca sempre di rendere tutto il più chiuso possibile: apri solo quello che davvero deve essere accessibile da fuori. Se un campo o metodo serve solo dentro la classe — fallo private. Solo se serve ai figli — protected. public — solo per i contratti.

Male:

public string ConnectionString; // Chiunque può cambiarlo!

Meglio:


private string _connectionString;
public string ConnectionString
{
    get => _connectionString;
    private set => _connectionString = value;
}

Uso delle proprietà automatiche

Con le proprietà automatiche e i init-only setters, scrivere le property "a mano" ormai è fuori moda.

Esempio:


public string Name { get; set; } // Ottimo!
public int Age { get; init; }    // Solo per l’inizializzazione, più sicuro.

Se ti servono proprietà calcolate:


public string FullName => $"{FirstName} {LastName}";

Incapsulamento e getter/setter

Se una proprietà ha senso di business per essere cambiata o controllata, usa un campo privato + getter/setter pubblico con logica.


private int _grade;
public int Grade
{
    get => _grade;
    set
    {
        if (value < 0) _grade = 0;
        else if (value > 100) _grade = 100;
        else _grade = value;
    }
}

Così non rischi di "rompere" l’oggetto per sbaglio.

7. Altri consigli utili

Non aver paura di interfacce e astrazioni

Le interfacce servono per contratti comodi, testing e per espandere l’app.

Male:

  • interfaccia con un solo metodo che non usa nessuno;
  • interfaccia implementata da una sola classe.
Bene:
  • interfaccia usata da 2+ classi;
  • interfaccia per astrazione di sistemi esterni (tipo logging, storage dati).

Scrivi codice facile da testare

Uno dei segni di buon codice OOP — è testabile.

  • Non fare metodi che dipendono da variabili globali o campi statici.
  • Non aver paura della dependency injection — passa le dipendenze nel costruttore (Dependency Injection).
  • Separa i calcoli (logica) dall’interazione con l’utente (input/output). Così testare e modificare l’app sarà molto più facile.

Lifehack e anti-pattern per chi inizia

  • Non scrivere "classe Dio" (God Object) che fa tutto.
  • Non mettere "numeri magici" senza spiegazione (if (status == 42) — perché 42?).
  • Non abusare dell’ereditarietà solo per ereditare — a volte meglio la composizione (classe con campi di altre classi, non erede).
  • Non scrivere metodi complicati da 100 righe — impossibili da testare e capire.
  • Lascia sempre spazio per estendere (open/closed principle).

8. Come appare codice brutto e codice bello

Esempio brutto:


class s // Male: nome classe minuscolo.
{
    public int a; // inutile, nome brutto
    public void m() // Male: metodo con nome di una lettera.
    {
        Console.WriteLine(a);
        // Male: non si capisce cosa fa il metodo.
    }
}

Esempio bello:


// Rappresenta uno studente.
public class Student
{
    // Età dello studente.    
    private int _age;
    public int Age
    {
        get => _age;
        set => _age = value < 0 ? 0 : value;
    }

    // Stampa le info dello studente.
    public void PrintInfo()
    {
        Console.WriteLine($"Età dello studente: {Age}");
    }
}
2
Compito
C# SELF, livello 25, lezione 4
Bloccato
Nominare correttamente classi e metodi
Nominare correttamente classi e metodi
2
Compito
C# SELF, livello 25, lezione 4
Bloccato
Organizzazione della struttura della classe
Organizzazione della struttura della classe
1
Sondaggio/quiz
Errori nell'ereditarietà, livello 25, lezione 4
Non disponibile
Errori nell'ereditarietà
Errori comuni quando dichiari classi e oggetti
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION