Nozioni di base sull'iniezione delle dipendenze
Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 46 di 70.
Dependency Injection (DI) è una tecnica in cui una classe riceve le sue dipendenze dall'esterno invece di crearle autonomamente. Questo rende il codice più flessibile, testabile e debolmente accoppiato.
Senza DI, una classe crea le proprie dipendenze, rendendo difficile modificarle o testarle:
public class OrderService
{
private EmailSender sender = new EmailSender(); // Strettamente accoppiato
public void PlaceOrder() => sender.Send("Order placed");
}Con la DI, le dipendenze vengono passate tramite il costruttore:
public interface IMessageSender
{
void Send(string message);
}
public class EmailSender : IMessageSender
{
public void Send(string message) => Console.WriteLine("Email: " + message);
}
public class OrderService
{
private IMessageSender sender;
public OrderService(IMessageSender sender)
{
this.sender = sender;
}
public void PlaceOrder() => sender.Send("Order placed");
}Ora OrderService non sa né gli interessa se sta usando email, SMS o un mittente simulato per i test. La dipendenza viene «iniettata» quando l'oggetto viene creato:
IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder(); // Email: Ordine effettuatoQuesto schema segue il principio di dipendere dalle astrazioni (interfacce) anziché dalle implementazioni concrete. Permette di sostituire le implementazioni senza modificare la classe che le utilizza, un aspetto essenziale per i test unitari e per mantenere architetture flessibili.
Sfida
FacileCreiamo un sistema di registrazione che dimostri la potenza dell’iniezione delle dipendenze. Invece di codificare rigidamente il modo in cui vengono scritti i log, creerai una classe flessibile Logger che può funzionare con qualsiasi destinazione di registrazione: console, simulazione di un file o qualsiasi altra cosa, senza conoscerne i dettagli.
Organizzerai il codice in tre file:
ILogWriter.cs: Definisci un’interfaccia chiamataILogWriternello spazio dei nomiLogging. Questa interfaccia rappresenta il contratto per qualsiasi destinazione di registrazione e dovrebbe avere un solo metodoWrite(string message)che restituisce una stringa che descrive dove è stato scritto il messaggio.LogWriters.cs: Crea due implementazioni concrete diILogWriternello spazio dei nomiLogging:ConsoleLogWriter- il suo metodoWriterestituisceConsole: {message}FileLogWriter- riceve un nome file tramite il costruttore e il suo metodoWriterestituisceFile[{filename}]: {message}
Logger.cs: Crea una classeLoggernello spazio dei nomiLoggingche riceve la propria dipendenza tramite l’iniezione nel costruttore. La classeLoggerdovrebbe:- Accettare un
ILogWritertramite il costruttore e memorizzarlo in un campo privato - Avere un metodo
Log(string message)che delega la chiamata al writer iniettato e restituisce il risultato
- Accettare un
Program.cs: Dimostra l’iniezione delle dipendenze creando due logger diversi: uno conConsoleLogWritere uno conFileLogWriter. Mostra come la stessa classeLoggersi comporta in modo diverso a seconda della dipendenza iniettata.
Riceverai due input:
- Il nome del file per il logger su file
- Il messaggio da registrare
Crea un Logger con ConsoleLogWriter e registra il messaggio. Poi crea un altro Logger con FileLogWriter (usando il nome del file fornito in input) e registra lo stesso messaggio. Stampa ogni risultato su una riga separata.
Per esempio, se gli input sono app.log e System started, l’output dovrebbe essere:
Console: System started
File[app.log]: System startedNota che la classe Logger non cambia mai: funziona con qualsiasi implementazione di ILogWriter. Questa è l’essenza dell’iniezione delle dipendenze: la classe Logger dipende da un’astrazione (l’interfaccia) e l’implementazione concreta viene «iniettata» dall’esterno. In seguito potresti aggiungere facilmente un DatabaseLogWriter o un CloudLogWriter senza modificare affatto la classe Logger!
Provalo tu
using System;
using Logging;
class Program
{
public static void Main(string[] args)
{
// Leggi i dati di input
string filename = Console.ReadLine();
string message = Console.ReadLine();
// TODO: Crea un Logger con un ConsoleLogWriter e registra il messaggio
// Stampa il risultato
// TODO: Crea un altro Logger con un FileLogWriter (usando filename) e registra lo stesso messaggio
// Stampa il risultato
}
}
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
1Fondamenti della programmazione orientata agli oggetti
File esterniNamespace e direttiveIntroduzione a classi e oggettiLa parola chiave "this"Metodi e parametriCampi e proprietàCostruttoriInizializzatori di oggettiRiepilogo - Calcolatrice semplice4Ereditarietà
Sintassi di base dell'ereditarietà (:)La parola chiave 'base'Parole chiave virtual e overrideClassi sealedLa classe base 'object'Riepilogo - Gerarchia dei dipendenti7Funzionalità avanzate
Sovraccarico degli operatoriIndicizzatori (this[])Override di ToString()Metodi di estensioneRiepilogo - Lista personalizzata10Pattern di progettazione Parte 1
Introduzione ai pattern di progettazioneSingleton thread-safePattern FactoryPattern Observer (eventi)Pattern Strategy2Proprietà e membri statici
Proprietà implementate automaticamenteProprietà di sola lettura e di sola scritturaCampi e metodi staticiClassi staticheMembri con corpo di espressione5Polimorfismo e interfacce
Polimorfismo a compile time e runtimeInterfaccia o classe astrattaInterfacce multipleInterfacce espliciteUpcasting e downcastingRipasso: calcolatore di forme8Concetti avanzati di programmazione a oggetti
Composizione invece dell'ereditarietàTipi generici (classi e metodi)Delegati ed eventiAttributi e reflectionIDisposable e istruzione usingNozioni di base sull'iniezione delle dipendenze11Pattern di progettazione, parte 2
Pattern del ComandoPattern dell’AdattatorePattern del DecoratorePattern Template MethodPattern dello StatoPattern Composito3Architettura delle classi
Dati di istanza vs staticiParole chiave 'readonly' e 'const'Campi di supportoRiepilogo - Gestore di conti bancari6Incapsulamento
Modificatori di accessoProprietà per l'incapsulamentoImplementazione dell'occultamento dei datiModelli di immutabilitàRiepilogo - Registri degli studentiEsercitati da solo: Compilatore C# online