Pattern Composito
Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 61 di 70.
Il pattern Composite è un pattern strutturale che consente di comporre oggetti in strutture ad albero e di trattare in modo uniforme gli oggetti singoli e i gruppi di oggetti. È perfetto per rappresentare gerarchie parte-tutto in cui vuoi lavorare sia con elementi singoli sia con raccolte tramite la stessa interfaccia.
Il pattern ha tre componenti chiave: un'interfaccia componente che definisce operazioni comuni, classi foglia che rappresentano singoli oggetti e classi composite che contengono figli e implementano la stessa interfaccia:
public interface IFileSystemItem
{
string Name { get; }
int GetSize();
}
public class File : IFileSystemItem
{
public string Name { get; }
private int _size;
public File(string name, int size)
{
Name = name;
_size = size;
}
public int GetSize() => _size;
}
public class Folder : IFileSystemItem
{
public string Name { get; }
private List<IFileSystemItem> _items = new List<IFileSystemItem>();
public Folder(string name) => Name = name;
public void Add(IFileSystemItem item) => _items.Add(item);
public int GetSize() => _items.Sum(item => item.GetSize());
}Il bello di questo schema è che i client non devono sapere se stanno lavorando con un singolo file o con un'intera gerarchia di cartelle:
var root = new Folder("root");
root.Add(new File("doc.txt", 100));
var subFolder = new Folder("images");
subFolder.Add(new File("photo.jpg", 500));
subFolder.Add(new File("icon.png", 50));
root.Add(subFolder);
Console.WriteLine(root.GetSize()); // 650Il pattern Composite è ideale per i file system, gli alberi di componenti dell’interfaccia utente, gli organigrammi o qualsiasi struttura in cui i contenitori e i contenuti debbano essere trattati allo stesso modo. Semplifica il codice client eliminando la necessità di distinguere tra elementi semplici e complessi.
Sfida
FacileCostruiamo un sistema di organigramma usando il pattern Composite. Creerai una struttura in cui sia i singoli dipendenti sia i reparti (che contengono dipendenti o altri reparti) possono essere trattati in modo uniforme, permettendoti di calcolare il costo totale degli stipendi a qualsiasi livello della gerarchia.
Organizzerai il codice in tre file:
OrgComponent.cs: definisci un'interfacciaIOrgComponentnello spazio dei nomiOrganizationcon due membri: una proprietàName(stringa) e un metodoGetSalary()che restituisce un intero. Questa interfaccia rappresenta il contratto comune seguito sia dai singoli dipendenti sia dai reparti.OrgClasses.cs: crea due classi nello stesso spazio dei nomi:Employee- una classe foglia che rappresenta un singolo lavoratore. Accetta un nome e uno stipendio nel costruttore e implementa l'interfaccia restituendo il proprio stipendio.Department- una classe composita che rappresenta un gruppo. Accetta un nome nel costruttore e mantiene un elenco di elementiIOrgComponent. Includi un metodoAdd(IOrgComponent component)per aggiungere membri. Il suo metodoGetSalary()deve restituire la somma degli stipendi di tutti i suoi membri.
Program.cs: crea una struttura organizzativa basata sull'input e calcola lo stipendio totale. Creerai reparti e dipendenti, li anniderai in modo appropriato e dimostrerai come il pattern Composite ti permette di trattare uniformemente l'intera gerarchia.
Riceverai i seguenti input:
- Il numero di componenti da creare
- Per ogni componente: il tipo (
employeeodepartment), seguito dal nome e, per i dipendenti, dal loro stipendio. Per i reparti, riceverai anche il nome del reparto genitore (orootse si tratta di un reparto di primo livello)
Formato dell'input per ogni componente:
employee
{name}
{salary}
{parent_department}
department
{name}
{parent_department}Dopo aver creato la struttura, stampa lo stipendio totale del reparto radice nel formato {root_name} Total Salary: {amount}.
Ad esempio, se gli input sono:
5
department
Engineering
root
employee
Alice
5000
Engineering
employee
Bob
4500
Engineering
department
QA
Engineering
employee
Charlie
4000
QAL'output dovrebbe essere:
Engineering Total Salary: 13500Il reparto Engineering contiene Alice (5000), Bob (4500) e il reparto QA. QA contiene Charlie (4000). Quando chiami GetSalary() su Engineering, il totale viene calcolato ricorsivamente a partire da tutti i componenti annidati: dimostrazione di come il pattern Composite ti permetta di lavorare con strutture ad albero complesse tramite un'interfaccia semplice e uniforme!
Provalo tu
using System;
using System.Collections.Generic;
using Organization;
class Program
{
public static void Main(string[] args)
{
int n = Convert.ToInt32(Console.ReadLine());
// TODO: Crea un dizionario per memorizzare i dipartimenti per nome
// Questo ti aiuterà a trovare i dipartimenti genitori quando aggiungi i componenti
Dictionary<string, Department> departments = new Dictionary<string, Department>();
// TODO: Tieni traccia del dipartimento radice
Department root = null;
for (int i = 0; i < n; i++)
{
string type = Console.ReadLine();
if (type == "department")
{
string name = Console.ReadLine();
string parent = Console.ReadLine();
// TODO: Crea il dipartimento
// TODO: Aggiungilo al dizionario departments
// TODO: Se parent è "root", questo è il dipartimento radice
// TODO: Altrimenti, trova il dipartimento genitore e aggiungici questo dipartimento
}
else if (type == "employee")
{
string name = Console.ReadLine();
int salary = Convert.ToInt32(Console.ReadLine());
string parent = Console.ReadLine();
// TODO: Crea il dipendente
// TODO: Trova il dipartimento genitore e aggiungici il dipendente
}
}
// TODO: Stampa lo stipendio totale del dipartimento radice
// Formato: "{root_name} Total Salary: {amount}"
Console.WriteLine($"{root.Name} Total Salary: {root.GetSalary()}");
}
}
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