Menu
Coddy logo textTech

Composizione invece dell'ereditarietà

Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 41 di 70.

L’ereditarietà crea uno stretto accoppiamento tra le classi. Quando una Car eredita da Vehicle, qualsiasi modifica a Vehicle può compromettere Car. La composizione offre un’alternativa più flessibile: invece di ereditare un comportamento, una classe contiene oggetti che forniscono quel comportamento.

Considera un personaggio di gioco che può muoversi e attaccare. Con l'ereditarietà, potresti creare una gerarchia complessa. Con la composizione, ti basta assegnare al personaggio i componenti di cui ha bisogno:

public class MovementBehavior
{
    public void Move() => Console.WriteLine("Moving...");
}

public class AttackBehavior
{
    public void Attack() => Console.WriteLine("Attacking!");
}

public class Character
{
    private MovementBehavior movement = new MovementBehavior();
    private AttackBehavior attack = new AttackBehavior();
    
    public void Move() => movement.Move();
    public void Attack() => attack.Attack();
}

La differenza fondamentale è la relazione. L'ereditarietà rappresenta «è-un» (un Dog è un Animal), mentre la composizione rappresenta «ha-un» (un Character ha un comportamento di movimento). La composizione ti permette di sostituire i comportamenti durante l'esecuzione, combinare liberamente le capacità ed evitare il fragile problema della classe base.

Usa l'ereditarietà quando esiste una vera relazione gerarchica. Scegli la composizione quando hai bisogno di flessibilità, quando i comportamenti potrebbero essere condivisi tra classi non correlate o quando vuoi modificare il comportamento in modo dinamico.

challenge icon

Sfida

Facile

Costruiamo un sistema di notifiche che dimostra la potenza della composizione rispetto all'ereditarietà. Invece di creare una complessa gerarchia di classi per le notifiche, comporrai un NotificationSender assegnandogli componenti di comportamento intercambiabili.

Organizzerai il codice in tre file:

  • Behaviors.cs: crea due classi di comportamento nello spazio dei nomi Notifications che gestiscono diversi aspetti dell'invio delle notifiche:
    • FormattingBehavior - ha un metodo Format(string message) che restituisce il messaggio racchiuso tra [ALERT] all'inizio e [END] alla fine
    • DeliveryBehavior - ha un metodo Deliver(string channel, string message) che restituisce una stringa nel formato Sending via {channel}: {message}
  • NotificationSender.cs: crea una classe NotificationSender nello spazio dei nomi Notifications che usa la composizione per combinare i comportamenti. Il tuo mittente deve:
    • contenere istanze sia di FormattingBehavior sia di DeliveryBehavior come campi privati
    • avere una proprietà Channel (string) che memorizza il nome del canale di consegna
    • avere un costruttore che accetta il nome del canale
    • avere un metodo Send(string message) che prima formatta il messaggio usando il comportamento di formattazione, poi lo invia usando il comportamento di consegna e restituisce il risultato finale
  • Program.cs: nel file principale, crea un NotificationSender con un canale preso dall'input, quindi invia un messaggio preso dall'input. Stampa il risultato per mostrare come funzionano insieme i comportamenti composti.

Riceverai due input:

  • Il nome del canale di consegna (ad es., Email, SMS)
  • Il messaggio da inviare

Stampa il risultato della chiamata a Send() sul tuo mittente di notifiche.

Ad esempio, se gli input sono Email e Server is down, l'output dovrebbe essere:

Sending via Email: [ALERT] Server is down [END]

Nota che NotificationSender non eredita nulla: semplicemente "ha" comportamenti di formattazione e di consegna. Questo approccio basato sulla composizione significa che potresti sostituire facilmente uno dei due comportamenti senza modificare la classe del mittente, oppure riutilizzare questi comportamenti in classi completamente diverse!

Provalo tu

using System;
using Notifications;

class Program
{
    public static void Main(string[] args)
    {
        // Leggi l'input
        string channel = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: Crea un NotificationSender con il canale
        
        // TODO: Invia il messaggio e stampa il risultato
        
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C# online