CodeGym /Corsi /C# SELF /Astratti nella costruzione delle gerarchie

Astratti nella costruzione delle gerarchie

C# SELF
Livello 22, Lezione 3
Disponibile

1. Introduzione

Proviamo a guardare l’astrazione dall’alto: immagina un grande ufficio dove diversi dipendenti fanno cose diverse. Magari hanno tutti il ruolo comune di "Dipendente", ma ognuno ha il suo compito speciale. Ma se tu, come capo, vuoi che ogni dipendente possa "EseguiLavoro", non ti interessa come lo fa: il programmatore scrive codice, il contabile stampa i report. Questa idea di "contratto comune per tutti tramite astrazione, dettagli per gli specialisti" è la base per costruire una gerarchia di classi fatta bene.

Come si vede nel codice?

Tutto parte da una classe astratta base. Descrive la cosa più importante che tutti i suoi figli devono saper fare.


public abstract class Employee
{
    public string Name { get; }
    public Employee(string name)
    {
        Name = name;
    }

    // Metodo astratto — contratto per tutti i dipendenti
    public abstract void DoWork();
}

Poi arrivano le classi derivate (figli) — programmatore e contabile:


public class Programmer : Employee
{
    public Programmer(string name) : base(name) { }
    public override void DoWork()
    {
        Console.WriteLine($"{Name} scrive codice");
    }
}

public class Accountant : Employee
{
    public Accountant(string name) : base(name) { }
    public override void DoWork()
    {
        Console.WriteLine($"{Name} conta i soldi");
    }
}

Ora guarda: possiamo creare una lista di dipendenti — contabili, programmatori, chi vuoi (l’astrazione non si oppone a questa evoluzione!).


Employee[] office = new Employee[]
{
    new Programmer("Vasya"),
    new Accountant("Tanya")
};

foreach (Employee emp in office)
{
    emp.DoWork(); // Ognuno fa il suo lavoro
}

Vasya scrive codice
Tanya conta i soldi

Nota quanto tutto è diventato bello, universale e soprattutto scalabile. Puoi aggiungere una nuova classe, tipo Manager, e il codice che già hai — non devi cambiarlo!

2. Come costruire gerarchie basate sulle astrazioni

Visto che quasi ogni libro di OOP non può vivere senza l’esempio degli animali, seguiamo la tradizione anche noi.

Classe astratta — nel ruolo di "Grande Dittatore"


public abstract class Animal
{
    public string Name { get; set; }
    public Animal(string name)
    {
        Name = name;
    }

    // Tutti gli animali devono saper fare un suono
    public abstract void MakeSound();

    // Ma non tutti devono saper volare, quindi:
    public virtual void Fly()
    {
        Console.WriteLine("Non posso volare.");
    }
}

Classi figlie: devono fare un suono, possono ridefinire altri metodi


public class Cat : Animal
{
    public Cat(string name) : base(name) { }
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}: Miao!");
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name) { }
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}: Bau!");
    }
}

public class Eagle : Animal
{
    public Eagle(string name) : base(name) { }
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}: Grido!");
    }

    public override void Fly()
    {
        Console.WriteLine($"{Name} vola nel cielo!");
    }
}

Ora proviamo a mettere insieme una collezione di animali diversi:


Animal[] zoo = new Animal[]
{
    new Cat("Barsik"),
    new Dog("Sharik"),
    new Eagle("Aquila")
};

foreach (Animal animal in zoo)
{
    animal.MakeSound();
    animal.Fly();
}

Barsik: Miao!
Non posso volare.
Sharik: Bau!
Non posso volare.
Aquila: Grido!
Aquila vola nel cielo!

Nota quanto è facile ora usare qualsiasi classe figlia: non pensi a che oggetto c’è nella collezione. L’astrazione si occupa del "contratto", i dettagli dell’implementazione — del comportamento concreto.

3. Astrazione su più livelli

L’astrazione non è solo divisione tra classe base e figli diretti. Nei sistemi complessi spesso si fa su più livelli, dove una classe astratta si basa su un’altra. È come una torta a strati (o una cipolla, come direbbe Shrek): ogni strato nasconde dettagli inutili, lasciando solo ciò che serve.

Esempio: Gerarchia dei mezzi di trasporto


.              Vehicle (abstract)
            /           |           \
       Car           Airplane      Boat
  ElectricCar    JetAirplane    SailBoat

Qui Vehicle definisce i principi comuni (tipo il metodo Move()), ma non sa come si muove una macchina o un aereo. Lo decidono le classi figlie. Le classi più specifiche, tipo ElectricCar o JetAirplane, possono aggiungere dettagli propri, ampliando le funzionalità.

Schema a blocchi della gerarchia:


.       +------------------+
        |  Vehicle         |  <--- classe astratta
        +------------------+
        /        \
   +-------+   +-------+
   |  Car  |   | Boat  |  <--- astrazioni intermedie o classi concrete
   +-------+   +-------+

Codice: classe astratta base


public abstract class Vehicle
{
    public string Model { get; }
    public Vehicle(string model)
    {
        Model = model;
    }

    // Metodo astratto
    public abstract void Move();
}

Classe astratta intermedia

A volte anche a livello intermedio servono astrazioni!


public abstract class Car : Vehicle
{
    public Car(string model) : base(model) { }

    public override void Move()
    {
        Console.WriteLine($"{Model} va sulla strada.");
    }

    // Metodo astratto — non tutte le auto sono elettriche:
    public abstract void RefuelOrCharge();
}

Implementazioni concrete


public class ElectricCar : Car
{
    public ElectricCar(string model) : base(model) { }

    public override void RefuelOrCharge()
    {
        Console.WriteLine($"{Model} si carica con l'elettricità.");
    }
}

public class GasolineCar : Car
{
    public GasolineCar(string model) : base(model) { }

    public override void RefuelOrCharge()
    {
        Console.WriteLine($"{Model} fa il pieno di benzina.");
    }
}

Risultato: l’astrazione su più livelli rende facile aggiungere nuove classi e funzioni.

Visualizzazione: esempio di gerarchia di classi

Classe Tipo Astratta Genitore Comportamento speciale
Vehicle Base Sì - Metodo astratto Move()
Car Intermedia Sì Vehicle Astratto RefuelOrCharge()
ElectricCar Finale No Car Implementazione RefuelOrCharge()
GasolineCar Finale No Car Implementazione RefuelOrCharge()
Boat Finale No Vehicle Implementazione Move()

4. Usare l’astrazione per applicazioni scalabili

Vantaggi pratici:

  1. Flessibilità ed estendibilità del codice: Puoi aggiungere una nuova classe (tipo un nuovo animale, mezzo di trasporto o dipendente) senza cambiare il codice già scritto. I tuoi cicli/metodi funzioneranno col nuovo oggetto in automatico — basta che implementi i metodi definiti nell’astrazione.
  2. Minimizzazione della duplicazione del codice: Proprietà e metodi comuni (tipo nome, logica base) si definiscono una volta nella classe astratta, e i figli li ereditano subito.
  3. Approccio usatissimo nei grandi framework e sistemi: Per esempio, in ASP.NET MVC ci sono controller base astratti (ControllerBase), e in WinForms la classe astratta Control per tutti gli elementi UI. Così puoi estendere il framework senza rompere le parti già scritte.

Dove serve?

  • Qualsiasi sistema grande: app bancarie (dipendenti, operazioni e transazioni), giochi (entità di gioco, personaggi), app business (cataloghi, prodotti, utenti), qualsiasi dato strutturato e gerarchico.
  • Colloqui tecnici: saper costruire classi basate su astrazioni e spiegare le gerarchie è una domanda tipica ai colloqui.
  • Architettura dei programmi: l’astrazione ti permette di costruire lo "scheletro" dell’architettura anche prima di scrivere la logica — comodo per lavoro di squadra, TDD (test driven development) e per la manutenzione.

5. Errori tipici e trappole nella costruzione delle gerarchie

Errore n°1: dimenticata l’implementazione del metodo astratto.
Il compilatore non ti lascia creare un’istanza di una classe derivata se non hai implementato tutti i membri astratti della base. È una regola obbligatoria — come una legge non scritta: vuoi essere concreto? Implementa tutto.

Errore n°2: tentativo di ereditarietà multipla da classi astratte.
In C# non puoi ereditare da più classi insieme, anche se sono tutte astratte. È un limite del linguaggio. Se ti serve "ereditare" comportamenti da fonti diverse — usa le interfacce. Sono come classi astratte, ma più leggere e senza implementazione.

Errore n°3: non capire il senso delle classi astratte intermedie.
Queste classi si usano spesso per raggruppare logica comune e proteggere dalla creazione di oggetti "non ben definiti". Per esempio, la classe Car può essere astratta per evitare che qualcuno crei solo una "macchina" senza specificare se è a benzina, diesel o elettrica.

2
Compito
C# SELF, livello 22, lezione 3
Bloccato
Creazione di una gerarchia di classi con una classe astratta
Creazione di una gerarchia di classi con una classe astratta
2
Compito
C# SELF, livello 22, lezione 3
Bloccato
Astrazione multilivello: mezzi di trasporto
Astrazione multilivello: mezzi di trasporto
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION