paint-brush
11 modelli di progettazione chiave: una guida essenzialedi@ssukhpinder
1,415 letture
1,415 letture

11 modelli di progettazione chiave: una guida essenziale

di Sukhpinder Singh33m2024/09/08
Read on Terminal Reader

Troppo lungo; Leggere

Scopri i segreti dell'architettura software con *Mastering Software Architecture: 11 Key Design Patterns Explained*. Il libro è scritto nel linguaggio di programmazione C# C# 8.0. Ci sono 11 design pattern che possono essere utilizzati per creare una factory per la creazione di factory. Il factory pattern astratto è un metodo factory di estensione; si consiglia di esaminare il metodo factory.
featured image - 11 modelli di progettazione chiave: una guida essenziale
Sukhpinder Singh HackerNoon profile picture
0-item
1-item
2-item
3-item
4-item

Scopri i segreti dell'architettura software con Mastering Software Architecture: 11 Key Design Patterns Explained .

Sommario

  1. Modello di progettazione — Fabbrica astratta
  • Obiettivi di apprendimento
  • Iniziare
  • Come utilizzare un provider di fabbrica astratta?
  • Produzione

2. Modello di progettazione — Adattatore

  • Caso d'uso
  • Obiettivi di apprendimento
  • Iniziare

3. Modello di progettazione — Costruttore

  • Caso d'uso
  • Obiettivi di apprendimento
  • Iniziare
  • Come utilizzare il modello builder dal metodo Main()
  • Produzione

4. Come utilizzare il modello della catena di responsabilità

  • Caso d'uso
  • Iniziare
  • Come utilizzare il modello della Catena di Responsabilità?
  • Produzione

5. Modello di progettazione — Decoratore

  • Caso d'uso
  • Obiettivi di apprendimento
  • Iniziare
  • Decorator Pattern in azione
  • Codice completo
  • Produzione

6. Modello di progettazione — Metodo di fabbrica

  • Obiettivi di apprendimento
  • Iniziare
  • Come utilizzare il metodo di fabbrica?
  • Produzione

7. Modello di progettazione — Iteratore

  • Caso d'uso
  • Iniziare
  • Modello Iteratore in azione
  • Produzione

8. Modello di progettazione — Mediatore

  • Caso d'uso
  • Obiettivi di apprendimento
  • Iniziare
  • Come utilizzare il modello mediatore dal metodo principale

9. Modello di progettazione — Osservatore

  • Caso d'uso
  • Obiettivi di apprendimento
  • Iniziare
  • Come utilizzare un modello di osservatore?
  • Produzione

10. Modello di proprietà avanzata C# 8.0

  • Iniziamo
  • Programma di ricerca di pattern con nuova sintassi switch
  • Programma di prova
  • Uscita della console

11. Modello di progettazione — Singleton

  • Obiettivi di apprendimento
  • Iniziare
  • Produzione
  • Sicurezza del filo

Modello di progettazione — Fabbrica astratta

Secondo Gang of Four, i modelli di fabbrica astratti possono essere considerati come la fabbrica per creare fabbriche.


Obiettivi di apprendimento

  • Cos'è il modello di progettazione Abstract Factory?
  • Come scrivere codice utilizzando il modello di progettazione abstract factory?
  • Come creare un fornitore di fabbrica?
  • Come creare un'applicazione client (dal metodo Main) che utilizza un provider di fabbrica

Prerequisiti

Il modello di fabbrica astratta è puramente un metodo di estensione della fabbrica; si consiglia di esaminare il metodo di fabbrica prima di comprendere la progettazione della fabbrica astratta.

  • Conoscenza di base dei concetti OOPS.
  • Conoscenza di qualsiasi linguaggio di programmazione.

Iniziare

Consideriamo lo stesso esempio di qualsiasi Banca con tipi di conto quali Risparmio e Conto corrente. Ora, implementiamo l'esempio precedente utilizzando il design pattern abstract factory.


Innanzitutto, implementare le interfacce ISavingAccount e ICurrentAccount come segue:


 public interface ISavingAccount{ } public interface ICurrentAccount{ }


Ereditare l'interfaccia nelle classi sottostanti


 public class CurrentAccount : ICurrentAccount { public CurrentAccount(string message) { Console.WriteLine(message); } } public class SavingsAccount : ISavingAccount { public SavingsAccount( string message) { Console.WriteLine(message); } }


Scriviamo una classe astratta con metodi astratti per ogni tipo di account.


 public abstract class AccountTypeFactory { public abstract ISavingAccount SavingAccountFactory(string message); public abstract ICurrentAccount CurrentAccountFactory(string message); }


Ora creiamo un'implementazione di fabbrica denominata "Bank1Factory", che fornisce l'implementazione di metodi astratti.


 public class Bank1Factory : AccountTypeFactory { public override ICurrentAccount CurrentAccountFactory(string message) { return new CurrentAccount(message); } public override ISavingAccount SavingAccountFactory(string message) { return new SavingsAccount(message); } }


Il modello di progettazione della fabbrica astratta differisce dal metodo della fabbrica in quanto deve implementare un provider di fabbriche, che restituisce le fabbriche come da definizione.


Ora che abbiamo creato tutte le astrazioni e le fabbriche. Progettiamo il provider di fabbrica. Di seguito è riportato il frammento di codice per il provider di fabbrica, in cui un metodo statico creerà una fabbrica in base al nome dell'account.


 public class AccountFactoryProvider { public static AccountTypeFactory GetAccountTypeFactory(string accountName) { if (accountName.Contains("B1")) { return new Bank1Factory(); } else return null; } }

Come utilizzare un Abstract Factory Provider?

Prendiamo ad esempio un elenco di numeri di conto in cui, se il nome di un conto è letteralmente " B1 ", verrà utilizzata l'istanza Bank1Factory restituita tramite il provider factory.

 static void Main(string[] args) { List<string> accNames = new List<string> { "B1-456", "B1-987", "B2-222" }; for (int i = 0; i < accNames.Count; i++) { AccountTypeFactory anAbstractFactory = AccountFactoryProvider.GetAccountTypeFactory(accNames[i]); if (anAbstractFactory == null) { Console.WriteLine("Invalid " + (accNames[i])); } else { ISavingAccount savingAccount = anAbstractFactory.SavingAccountFactory("Hello saving"); ICurrentAccount currentAccount = anAbstractFactory.CurrentAccountFactory("Hello Current"); } } Console.ReadLine(); }


Se il nome dell'account non contiene il letterale "B1", il programma genererà un {{accountName}} non valido

Produzione

Di seguito è riportato l'output del frammento di codice sopra riportato.


 Hello saving B1-456 Hello Current B1-456 Hello saving B1-987 Hello Current B1-987

Modello di progettazione — Adattatore

Secondo Gang of Four, l'Adapter Pattern converte le interfacce di una classe nelle interfacce richieste dal client.


In altre parole, il modello di progettazione dell'adattatore aiuta le interfacce incompatibili a lavorare insieme.

Caso d'uso

Consideriamo un esempio di due organizzazioni che si fondono; l'organizzazione X sta prendendo il controllo di Y, ma durante la combinazione del codice, le interfacce non sono compatibili. Supponiamo che l'interfaccia che fornisce un elenco di transazioni dell'organizzazione Y non sia compatibile con X.


Quindi, il modello di progettazione dell'adattatore aiuta a risolvere questo problema, la cui implementazione è molto semplice.

Obiettivi di apprendimento

  • Come scrivere codice utilizzando un pattern di progettazione adattatore?

Iniziare

Creiamo un elenco di transazioni dall'organizzazione Y che vengono convertite in pattern richiesti dall'applicazione client dell'organizzazione X. La classe di cui sopra è nota come "Adaptee".


 public class OrgYTransactions { public List<string> GetTransactionsList() { List<string> transactions = new List<string>(); transactions.Add("Debit 1"); transactions.Add("Debit 2"); transactions.Add("Debit 3"); return transactions; } }


In secondo luogo, creiamo un'interfaccia di destinazione.


 public interface ITransactions{ List<string> GetTransactions(); }


Ora, infine, implementiamo la classe adattatore come segue.


 public class TransAdapter : OrgYTransactions, ITransactions { public List<string> GetTransactions() { return GetTransactionsList(); } }


Dopo aver completato tutte le implementazioni di cui sopra, vediamo come utilizzare la classe adattatore in un'applicazione console.


 class Program { static void Main(string[] args) { ITransactions adapter = new TransAdapter(); foreach (var item in adapter.GetTransactions()) { Console.WriteLine(item); } } }


Se si osserva attentamente l'utilizzo sottostante, abbiamo utilizzato l'interfaccia di destinazione ITransactions e la classe adattatore TransAdapter senza considerare come appaiono le interfacce della classe di terze parti OrgYTransactions. Questa è la potenza del design pattern dell'adattatore: converte le interfacce di una classe in interfacce richieste dal client.

Modello di progettazione — Costruttore

Secondo Gang of Four, un modello creazionale "Builder" consente di separare e riutilizzare un metodo specifico per costruire qualcosa.


Caso d'uso

Prendiamo l'esempio di un'auto: l'utente vuole realizzare due modelli, ovvero un SUV e una berlina.


Il modello di progettazione Builder risulta utile nel caso d'uso sopra descritto. Vediamo una dimostrazione dettagliata.


La classe Car ha le seguenti proprietà.

 public class Car{ public string Name { get; set; } public double TopSpeed { get; set; } public bool IsSUV { get; set; } }

Obiettivi di apprendimento

  • Come programmare utilizzando un modello di progettazione builder?

Iniziare

Per prima cosa, implementiamo un generatore di classi astratto esteso da diversi modelli di auto, come SUV o berline, a seconda del caso d'uso.


 public abstract class CarBuilder { protected readonly Car _car = new Car(); public abstract void SetName(); public abstract void SetSpeed(); public abstract void SetIsSUV(); public virtual Car GetCar() => _car; }


La classe astratta è composta dai seguenti metodi

  • Metodi astratti per ciascuna proprietà della classe Car.
  • Un metodo virtuale che restituisce l'istanza della classe Car.


Ora creiamo una fabbrica che utilizza la classe CarBuilder per costruire diversi modelli di auto e restituisce l'istanza dell'auto realizzata.


 public class CarFactory { public Car Build(CarBuilder builder) { builder.SetName(); builder.SetSpeed(); builder.SetIsSUV(); return builder.GetCar(); } }


Infine, implementare diversi modelli di automobili.

ModelloSuv.cs

 public class ModelSuv : CarBuilder { public override void SetIsSUV() { _car.IsSUV = true; } public override void SetName() { _car.Name = "Maruti SUV"; } public override void SetSpeed() { _car.TopSpeed = 1000; } }

ModelloSedan.cs

 public class ModelSedan : CarBuilder { public override void SetIsSUV() { _car.IsSUV = false; } public override void SetName() { _car.Name = "Maruti Sedan"; } public override void SetSpeed() { _car.TopSpeed = 2000; } }

Come utilizzare il modello di costruttore dal metodo Main()

Infine, utilizziamo i design pattern per costruire diversi modelli di auto con l'aiuto del metodo factory.Build(<model>).


 static void Main(string[] args) { var sedan = new ModelSedan(); var suv = new ModelSuv(); var factory = new CarFactory(); var builders = new List<CarBuilder> { suv, sedan }; foreach (var b in builders) { var c = factory.Build(b); Console.WriteLine($"The Car details" + $"\n--------------------------------------" + $"\nName: {c.Name}" + $"\nIs SUV: {c.IsSUV}" + $"\nTop Speed: {c.TopSpeed} mph\n"); } }

L'utilizzo sopra riportato mostra con quanta eleganza possiamo costruire diversi modelli di auto utilizzando il modello di progettazione builder.


Il pattern di codice è altamente gestibile ed estensibile. Se in futuro dovessimo sviluppare un nuovo modello, basterà che il nuovo modello estenda la classe CarBuilder, ed è fatta.

Produzione

Come utilizzare il modello della catena di responsabilità

Secondo Gang of Four, definisce una catena di responsabilità per elaborare una richiesta. In altre parole, passa la richiesta da un oggetto a un altro finché un oggetto non accetta la sua responsabilità.


Caso d'uso

Consideriamo un esempio di un sistema di reclami in una qualsiasi azienda aziendale. Ecco l'elenco della fascia di prezzo che può essere approvata e da chi.


 100–1000 Rs => Junior/Senior Engineers => Approved by Manager 1001–10000 Rs => Managers => Approved by Senior Manager


Se l'importo è al di fuori dell'intervallo di 10.000, è richiesta un'approvazione eccezionale da parte del dirigente senior.


Il caso d'uso di cui sopra può essere facilmente implementato utilizzando il design pattern Chain of Responsibility. Quindi, la classe claim ha le seguenti proprietà.


 public class Claim{ public int Id{get;set;} public double amount{get;set;} }

Iniziare

Innanzitutto, definiamo quali funzioni può svolgere un approvatore di reclami e stabiliamo una gerarchia per i dipendenti a diversi livelli. Implementiamo una classe astratta come mostrato di seguito


 public abstract class ClaimApprover { protected ClaimApprover claimApprover; public void SetHierarchy(ClaimApprover claimApprover) { this.claimApprover = claimApprover; } public abstract void ApproveRequest(Claim claim); }


In base al caso d'uso, guidiamo il claim requester della classe "junior/senior". Nota che questa classe/designazione di dipendenti non può approvare alcun claim.


 public class Junior : ClaimApprover { public override void ApproveRequest(Claim claim) { System.Console.WriteLine("Cannot approve"); } }


Allo stesso modo, definiamo l'implementazione per i ruoli di Manager e Senior Manager.


 public class Manager : ClaimApprover { public override void ApproveRequest(Claim claim) { if (claim.amount >= 100 && claim.amount <= 1000) { System.Console.WriteLine($"Claim reference {claim.Id} with amount {claim.amount} is approved by Manager"); } else if (claimApprover != null) { claimApprover.ApproveRequest(claim); } } }


Si noti che, in base all'intervallo di importo, se rientra nell'intervallo del Manager, il reclamo può essere approvato dal Manager; in caso contrario, la richiesta verrà inoltrata al Senior Manager.


 public class SeniorManager : ClaimApprover { public override void ApproveRequest(Claim claim) { if (claim.amount > 1000 && claim.amount <= 10000) { System.Console.WriteLine($"Claim reference {claim.Id} with amount {claim.amount} is approved by Senior Manager"); } else { System.Console.WriteLine($"Exceptional approval for Claim reference {claim.Id} with amount {claim.amount} is approved by Senior Manager"); } } }


Allo stesso modo, se l'intervallo di importo rientra nell'intervallo del Senior Manager, la richiesta può essere approvata dal Manager; in caso contrario, essendo l'ultimo nella gerarchia, viene effettuata un'approvazione eccezionale per un importo al di fuori dell'intervallo.


 ClaimApprover junior = new Manager(); ClaimApprover sukhpinder = new Manager(); ClaimApprover singh = new SeniorManager(); junior.SetHierarchy(sukhpinder); sukhpinder.SetHierarchy(singh); Claim c1 = new Claim() { amount = 999, Id = 1001 }; Claim c2 = new Claim() { amount = 10001, Id = 1002 }; junior.ApproveRequest(c1); sukhpinder.ApproveRequest(c2);

Come utilizzare il modello della catena di responsabilità?

  1. Definisci l'approvatore di reclami: junior, anche se non può approvare alcun reclamo.
  2. Definizione di approvatore di reclami: gestore "sukhpinder".
  3. Definizione di approvatore dei reclami: senior manager "Singh".
  4. Impostare una relazione gerarchica per il livello junior, ovvero l'approvatore dei reclami è il manager.
  5. Impostare una relazione gerarchica per il responsabile, ovvero l'approvatore dei reclami è il responsabile senior.
  6. Creare due diverse serie di reclami.
  7. Junior invia la richiesta di risarcimento al responsabile.
  8. Il responsabile invia la richiesta di risarcimento al responsabile senior.

Produzione

 Claim reference 1001 with amount 999 is approved by Manager Exceptional approval for Claim reference 1002 with amount 10001 is approved by Senior Manager


Per l'output della riga 1, l'importo rientrava nell'intervallo, quindi il responsabile lo ha approvato.


Per quanto riguarda l'output della linea 2, nonostante l'approvazione del responsabile senior, l'importo era fuori dall'intervallo.

Modello di progettazione — Decoratore

Secondo Gang of Four, il modello aggiunge dinamicamente ulteriori responsabilità a un oggetto di classe.


Caso d'uso

Consideriamo l'esempio dell'acquisto di un'auto del valore di dieci lakh; l'azienda offre le seguenti caratteristiche aggiuntive.

  • Tettuccio apribile
  • Sistema musicale avanzato
  • e molti altri


Con alcune funzionalità aggiuntive, il prezzo totale dell'auto aumenta. Implementiamo il caso d'uso di cui sopra utilizzando il Decorator Pattern.

Obiettivi di apprendimento

  • Come scrivere codice utilizzando un pattern di progettazione decoratore?

Iniziare

Implementiamo il caso d'uso definito sopra. Innanzitutto, definiamo una classe astratta Car e i suoi metodi base.


 public abstract class Car{ public abstract int CarPrice(); public abstract string GetName(); }


Consideriamo una piccola automobile che si estende al di sopra della classe astratta Auto.


 public class SmallCar : Car{ public override int CarPrice() => 10000; public override string GetName() => "Alto Lxi"; }


Ora implementiamo la classe CarDecorator utilizzando il componente Car.


 public class CarDecorator : Car { protected Car _car; public CarDecorator(Car car) { _car = car; } public override int CarPrice() => _car.CarPrice(); public override string GetName() =>_car.GetName(); }


Ora creiamo una classe separata per ogni funzionalità aggiuntiva disponibile per Car che eredita la classe CarDecorator.


A seconda del caso d'uso, le caratteristiche aggiuntive sono un tettuccio apribile e un sistema musicale avanzato.

AdvanceMusic.cs

Sostituisci i metodi come

  • Aggiungere al prezzo totale dell'auto il costo aggiuntivo di un "sistema musicale avanzato".

  • Aggiorna il nome dell'auto con il nome della caratteristica aggiuntiva.

     public class AdvanceMusic : CarDecorator { public AdvanceMusic(Car car) : base(car) { } public override int CarPrice() => _car.CarPrice() + 3000; public override string GetName()=> "Alto Lxi with advance music system"; }

Tettuccio apribile. cs

Sostituisci i metodi come

  • Aggiungere al prezzo totale dell'auto il costo aggiuntivo del "tetto apribile".
  • Aggiorna il nome dell'auto con il nome della caratteristica aggiuntiva.
 public class Sunroof : CarDecorator { public Sunroof(Car car) : base(car) { } public override int CarPrice() => _car.CarPrice() + 2000; public override string GetName() => "Alto Lxi with Sunroof"; }

Decorator Pattern in azione

Crea un'istanza di SmallCar e restituisci il nome e il prezzo dell'auto.


 Car car = new SmallCar(); Console.WriteLine($"Price of car {car.GetName()} : " + car.CarPrice());


Ora, aggiungiamo funzionalità aggiuntive come mostrato di seguito


 var car1 = new Sunroof(car); var car2 = new AdvanceMusic(car);

Codice completo

 static void Main(string[] args) { Car car = new SmallCar(); Console.WriteLine($"Price of car {car.GetName()} : " + car.CarPrice()); var car1 = new Sunroof(car); Console.WriteLine($"Price of car {car1.GetName()} : " + car1.CarPrice()); var car2 = new AdvanceMusic(car); Console.WriteLine($"Price of car {car2.GetName()} : " + car2.CarPrice()); }

Produzione

Congratulazioni! Hai implementato con successo il caso d'uso utilizzando il pattern decoratore.

Modello di progettazione — Metodo di fabbrica

Secondo la Gang of Four, il metodo factory consente alla sottoclasse di determinare quale oggetto di classe deve essere creato.


Obiettivi di apprendimento

  • Cos'è il modello di progettazione del metodo factory?
  • Come scrivere codice utilizzando il metodo factory?

Iniziare

Consideriamo un esempio di qualsiasi Banca con tipi di conto come Conti di risparmio e Conti correnti. Ora, implementiamo l'esempio precedente utilizzando il modello di progettazione factory


Per prima cosa, crea una classe astratta di tipo account.


 public abstract class AccoutType { public string Balance { get; set; } }


Implementare le classi di conto corrente e di risparmio ereditando la classe astratta AccountType come mostrato di seguito.


 public class SavingsAccount : AccoutType { public SavingsAccount() { Balance = "10000 Rs"; } } public class CurrentAccount : AccoutType { public CurrentAccount() { Balance = "20000 Rs"; } }


Infine, implementiamo l'interfaccia factory, che fornirà un contratto che aiuta a creare un oggetto di classe. Questa interfaccia è anche nota come Creator.


 public interface IAccountFactory { AccoutType GetAccoutType(string accountName); }


Infine, scrivi un'implementazione del metodo dell'interfaccia creator come mostrato di seguito. La classe che implementa il creator è nota come Concrete Creator.


 public class AccountFactory : IAccountFactory { public AccoutType GetAccoutType(string accountName) { if (accountName.Equals("SAVINGS", StringComparison.OrdinalIgnoreCase)) { return new SavingsAccount(); } else if (accountName.Equals("CURRENT", StringComparison.OrdinalIgnoreCase)) { return new CurrentAccount(); } else { throw new ArgumentException("Invalid account name"); } } }


Ecco fatto. Hai implementato con successo il metodo factory usando l'esempio Bank.

Come utilizzare il metodo Factory?

Una sottoclasse deciderà quale oggetto della classe "AccountType" verrà creato in base al nome dell'account.


 class Program { static void Main(string[] args) { IAccountFactory accountFactory = new AccountFactory(); var savingAccount = accountFactory.GetAccoutType("SAVINGS"); Console.WriteLine("Saving account balance: " + savingAccount.Balance); var currentAccount = accountFactory.GetAccoutType("CURRENT"); Console.WriteLine("Current account balance: " + currentAccount.Balance); } }

Ad esempio, se il nome dell'account è "RISPARMI", verrà creato e restituito l'oggetto classe "SavingAccount".


Allo stesso modo, se il nome dell'account è "CURRENT", verrà istanziato e restituito l'oggetto classe "CurrentAccount".

Produzione

 Saving account balance: 10000 Rs Current account balance: 20000 Rs

Modello di progettazione — Iteratore

Secondo Gang of Four, il modello iteratore fornisce un processo per ottenere l'oggetto aggregatore senza conoscerne l'implementazione.


Caso d'uso

Prendiamo come esempio una lista di raccolta di automobili e string[] un array di motociclette; dobbiamo progettare un oggetto aggregatore in modo che sia possibile scorrere la raccolta senza sapere se si tratta di una lista o di un array.


Il modello di progettazione iteratore aiuta a risolvere questo problema in cui un iteratore standard attraversa diversi tipi di raccolta.

Iniziare

Considerando il caso d'uso di cui sopra, definiamo un'interfaccia iteratore personalizzata che agisca come un livello astratto sull'iteratore di elenchi e array.


 public interface IVehicleIterator{ void First(); bool IsDone(); string Next(); string Current(); }


Ora, scrivi gli iteratori per auto e moto che implementano l'interfaccia di cui sopra in base al caso d'uso.

CarIterator.cs

 public class CarIterator : IVehicleIterator { private List<string> _cars; private int _current; public CarIterator(List<string> cars) { _cars = cars; _current = 0; } public string Current() { return _cars.ElementAt(_current); } public void First() { _current = 0; } public bool IsDone() { return _current >= _cars.Count; } public string Next() { return _cars.ElementAt(_current++); } }


L'iteratore car è implementato sulla raccolta List<string> e fornisce un'implementazione dei metodi di interfaccia.

MotociclettaIterator.cs

L'iteratore della motocicletta è implementato sulla raccolta string[] e fornisce un'implementazione dei metodi di interfaccia.


 public class MotercycleIterator : IVehicleIterator { private string[] _motercylces; private int _current; public MotercycleIterator(string[] motercylces) { _motercylces = motercylces; _current = 0; } public string Current() { return _motercylces[_current]; } public void First() { _current = 0; } public bool IsDone() { return _current >= _motercylces.Length; } public string Next() { return _motercylces[_current++]; } }


Dopo aver definito tutti gli iteratori sopra indicati, definire un'interfaccia standard dell'oggetto aggregatore che crei gli iteratori.


 public interface IVehicleAggregate{ IVehicleIterator CreateIterator(); }


Infine, annota le classi che implementano l'interfaccia aggregatore di cui sopra. In base al caso d'uso, sia le classi Car che Motorcycle implementeranno l'interfaccia aggregatore.

Auto. cs

Il metodo dell'interfaccia aggregatrice restituisce l'iteratore pertinente come mostrato di seguito.


 public class Car : IVehicleAggregate { private List<string> _cars; public Car() { _cars = new List<string> { "Car 1", "Car 2", "Car 3" }; } public IVehicleIterator CreateIterator() { return new CarIterator(_cars); } }

Motocicletta. cs

Il metodo dell'interfaccia aggregatrice restituisce l'iteratore pertinente come mostrato di seguito.


 public class Motercycle : IVehicleAggregate { private string[] _motercycles; public Motercycle() { _motercycles = new[] { "Bike 1", "Bike 2", "Bike 3" }; } public IVehicleIterator CreateIterator() { return new MotercycleIterator(_motercycles); } }

Modello Iteratore in azione

I metodi PrintVehicles verificano se !iterator.isDone e quindi restituiscono l'elemento della raccolta. Indipendentemente dalla raccolta con cui abbiamo a che fare, implementa metodi come First, IsDone e Next.


 static void Main(string[] args) { IVehicleAggregate car = new Vehicles.Car(); IVehicleAggregate motercycle = new Vehicles.Motercycle(); IVehicleIterator carIterator = car.CreateIterator(); IVehicleIterator motercycleIterator = motercycle.CreateIterator(); PrintVehicles(carIterator); PrintVehicles(motercycleIterator); } static void PrintVehicles(IVehicleIterator iterator) { iterator.First(); while (!iterator.IsDone()) { Console.WriteLine(iterator.Next()); } }

Produzione

Non conosciamo il tipo di raccolta sottostante, ma viene comunque iterato tramite l'Iterator Design Pattern. Se si procede con l'esecuzione, viene visualizzato il seguente output.

Modello di progettazione — Mediatore

Secondo Gang of Four, il modello Mediator incapsula l'interazione degli oggetti tra loro.


Il modello di progettazione del mediatore ci aiuta a progettare applicazioni debolmente accoppiate incapsulando le interazioni degli oggetti.

Caso d'uso

Consideriamo l'esempio di una chatroom in cui i partecipanti si registrano e come comunicare in modo efficiente.


È necessario implementare la seguente conversazione nella chatroom utilizzando il Mediator Design Pattern.


 David to Scott: 'Hey' Scott to David: 'I am good how about you.' Jennifer to Ashley: 'Hey ashley... david is back in the group' Jennifer to David: 'Where have you been?' Ashley to David: 'How come you aren't active here anymore?'

Obiettivi di apprendimento

  • Come programmare utilizzando un modello di progettazione mediatore?

Iniziare

Il primo passo è creare un elenco di nomi utente che saranno utilizzati all'interno di una chatroom. Un enum pubblico per questo è mostrato di seguito.


 public enum Username{ Ashley, David, Jennifer, Scott }


Ora, per prima cosa, implementiamo un livello astratto della chatroom.


 public abstract class AChatroom { public abstract void Register(User user); public abstract void Post(string fromUser, string toUser, string msg); }


E una classe che definisce metodi astratti. I metodi convalidano se l'utente esiste nel dizionario. Ad esempio, il metodo register convalida se l'utente esiste già o meno. Se non esiste, allora registra solo l'utente nella chatroom.


 public class Chatroom : AChatroom { private Dictionary<string, User> _users = new Dictionary<string, User>(); public override void Post(string fromUser, string toUser, string msg) { User participant = _users[toUser]; if (participant != null) { participant.DM(fromUser, msg); } } public override void Register(User user) { if (!_users.ContainsValue(user)) { _users[user.Name] = user; } user.Chatroom = this; } }


Infine, implementiamo le azioni che l'utente può eseguire, come inviare un messaggio a un utente nella chatroom o ricevere un messaggio diretto da un altro utente.


 public class User { private Chatroom _chatroom; private string _name; public User(string name) => this._name = name; public string Name => _name; public Chatroom Chatroom { set { _chatroom = value; } get => _chatroom; } public void Post(string to, string message) => _chatroom.Post(_name, to, message); public virtual void DM(string from, string message) => Console.WriteLine("{0} to {1}: '{2}'", from, Name, message); }

Come utilizzare il modello Mediator dal metodo principale

 static void Main(string[] args) { Chatroom chatroom = new Chatroom(); User Jennifer = new UserPersona(Username.Jennifer.ToString()); User Ashley = new UserPersona(Username.Ashley.ToString()); User David = new UserPersona(Username.David.ToString()); User Scott = new UserPersona(Username.Scott.ToString()); chatroom.Register(Jennifer); chatroom.Register(Ashley); chatroom.Register(David); chatroom.Register(Scott); David.Post(Username.Scott.ToString(), "Hey"); Scott.Post(Username.David.ToString(), "I am good how about you."); Jennifer.Post(Username.Ashley.ToString(), "Hey ashley... david is back in the group"); Jennifer.Post(Username.David.ToString(), "Where have you been?"); Ashley.Post(Username.David.ToString(), "How come you aren't active here anymore?"); Console.ReadKey(); }


  1. Viene creato l'oggetto classe Chatroom.
  2. Vengono creati quattro utenti diversi con nomi univoci.
  3. Registra ognuno di loro nella chatroom.
  4. Ora gli utenti possono iniziare a scambiarsi messaggi.

L'esecuzione del programma descrive solo il metodo Post della classe utente.


Output: la cronologia della chatroom dell'esecuzione del programma sopra


 David to Scott: 'Hey' Scott to David: 'I am good how about you.' Jennifer to Ashley: 'Hey ashley... david is back in the group' Jennifer to David: 'Where have you been?' Ashley to David: 'How come you aren't active here anymore?'

Modello di progettazione — Osservatore

Secondo Gang of Four, il pattern observer definisce la dipendenza tra due o più oggetti. Quindi, quando cambia lo stato di un oggetto, tutti i suoi dipendenti vengono notificati.


In altre parole, una modifica in un oggetto avvia la notifica in un altro oggetto.

Caso d'uso

Prendiamo l'esempio di un influencer di Instagram che ha un numero di follower pari a " x ". Quindi, nel momento in cui la celebrità aggiunge un post, tutti i follower vengono avvisati.


Implementiamo il caso d'uso sopra menzionato utilizzando l'Observer Design Pattern.

Obiettivi di apprendimento

  • Come programmare utilizzando un pattern di progettazione Observer?

Iniziare

In base al caso d'uso, il primo implementa un'interfaccia che contiene le azioni che una celebrità può eseguire. È noto come " Oggetto ".


 public interface ICelebrityInstagram{ string FullName { get; } string Post { get; set; } void Notify(string post); void AddFollower(IFollower fan); void RemoveFollower(IFollower fan); }

L'oggetto contiene le seguenti funzioni membro.

  • Notifica: per avvisare tutti i follower.

  • AggiungiFollower: aggiungi un nuovo follower all'elenco delle celebrità.

  • RemoveFollower: rimuove un follower dall'elenco delle celebrità.


Ora, implementa l'interfaccia dell'osservatore "IFollower", che contiene la funzione membro "Update" per la notifica.


 public interface IFollower{ void Update(ICelebrityInstagram celebrityInstagram); }


Infine, è il momento di implementare l'“Implementazione concreta” sia per il “ Soggetto ” che per l'“ Osservatore ”.

ConcreteObserver denominato “Follower.cs”

Fornisce un'implementazione della funzione membro Update, che invia il nome della celebrità e il post alla console.


 public class Follower : IFollower { public void Update(ICelebrityInstagram celebrityInstagram) { Console.WriteLine($"Follower notified. Post of {celebrityInstagram.FullName}: " + $"{celebrityInstagram.Post}"); } }

ConcreteSubject denominato “Sukhpinder. cs”

 public class Sukhpinder : ICelebrityInstagram { private readonly List<IFollower> _posts = new List<IFollower>(); private string _post; public string FullName => "Sukhpinder Singh"; public string Post { get { return _post; } set { Notify(value); } } public void AddFollower(IFollower follower) { _posts.Add(follower); } public void Notify(string post) { _post = post; foreach (var item in _posts) { item.Update(this); } } public void RemoveFollower(IFollower follower) { _posts.Remove(follower); } }

Come vedere un modello di osservatore?

Il seguente caso d'uso mostra che ogni volta che viene eseguita l'istruzione seguente sukhpinder.Post = "I love design patterns."; Il metodo di aggiornamento viene attivato per ogni follower, ovvero ogni oggetto follower viene informato di un nuovo post da "Sukhpinder".


 static void Main(string[] args) { var sukhpinder = new Sukhpinder(); var firstFan = new Follower(); var secondFan = new Follower(); sukhpinder.AddFollower(firstFan); sukhpinder.AddFollower(secondFan); sukhpinder.Post = "I love design patterns."; Console.Read(); }

Produzione

Modello di proprietà avanzata C# 8.0

L'articolo descrive come il pattern matching fornisca un metodo efficace per utilizzare ed elaborare tali dati in moduli che non facevano parte del sistema primario.


Iniziamo

Prendiamo l'esempio di Toll Calculator e vediamo come il pattern matching aiuta a scrivere un algoritmo per questo scopo.

Classe entità utilizzata in tutto l'articolo

 public class Car { public int PassengerCount { get; set; } } public class DeliveryTruck { public int Weight { get; set; } } public class Taxi { public int Fare { get; set; } } public class Bus { public int Capacity { get; set; } public int RidersCount { get; set; } }


Esempio 1: Calcolare il pedaggio in base alle seguenti condizioni:


  • Se il veicolo è un'auto => 100 Rs
  • Se il veicolo è DeliveryTruck => 200 Rs
  • Se il veicolo è un autobus => 150 Rs
  • Se il veicolo è un taxi => 120 Rs

Programma di corrispondenza di pattern con la nuova sintassi Switch

Se il tipo di veicolo corrisponde a Car 100 viene restituito e così via. Nota che null e {} sono casi predefiniti per il tipo di oggetto.

Inoltre, “_” può essere utilizzato per programmare lo scenario predefinito. Fare riferimento alla nuova sintassi dello switch.


È un modo molto più pulito ed efficiente di codificare e si consiglia inoltre di utilizzare nomi di variabili composti da una sola lettera all'interno della sintassi switch.


 public static int TollFare(Object vehicleType) => vehicleType switch { Car c => 100, DeliveryTruck d => 200, Bus b => 150, Taxi t => 120, null => 0, { } => 0 };

Prova il programma sopra

Esempi di test dal punto di vista di un'applicazione console. Il codice seguente illustra come chiamare la funzione di pattern-matching di cui sopra dal metodo principale.


 var car = new Car(); var taxi = new Taxi(); var bus = new Bus(); var truck = new DeliveryTruck(); Console.WriteLine($"The toll for a car is {TollFare(car)}"); Console.WriteLine($"The toll for a taxi is {TollFare(taxi)}"); Console.WriteLine($"The toll for a bus is {TollFare(bus)}"); Console.WriteLine($"The toll for a truck is {TollFare(truck)}");

Uscita della console

 The toll for a car is 100 The toll for a taxi is 120 The toll for a bus is 150 The toll for a truck is 200


Esempio 2: aggiungere il prezzo di occupazione in base al tipo di veicolo


  • Le auto e i taxi senza passeggeri pagano un extra di 10 rupie.
  • Auto e taxi con due passeggeri usufruiscono di uno sconto di 10 rupie.
  • Le auto e i taxi con tre o più passeggeri usufruiscono di uno sconto di 20 rupie.
  • Gli autobus che trasportano meno del 50% di passeggeri pagano un extra di 30 rupie.
  • Gli autobus con più del 90% di passeggeri hanno diritto a uno sconto di 40 rupie.
  • Per i camion di peso superiore a 5000 libbre verrà addebitato un supplemento di 100 rupie.
  • Per i camion leggeri sotto le 3000 libbre è previsto uno sconto di 20 rupie.

Interruttore di corrispondenza del modello

Fare riferimento alla sintassi di pattern-matching con classi di proprietà singole e multiple. Link

Corrispondenza di modelli — Entità auto

 Car { PassengerCount: 0 } => 100 + 10, Car { PassengerCount: 1 } => 100, Car { PassengerCount: 2 } => 100 - 10, Car c => 100 - 20,

Corrispondenza di modelli — Entità Taxi

 Taxi {Fare:0 }=>100+10, Taxi { Fare: 1 } => 100, Taxi { Fare: 2 } => 100 - 10, Taxi t => 100 - 20,

Corrispondenza di pattern — Entità bus

 Bus b when ((double)b.RidersCount / (double)b.Capacity) < 0.50 => 150 + 30, Bus b when ((double)b.RidersCount / (double)b.Capacity) > 0.90 => 150 - 40, Bus b => 150,

Corrispondenza di modelli — Entità del camion di consegna

 DeliveryTruck t when (t.Weight > 5000) => 200 + 100, DeliveryTruck t when (t.Weight < 3000) => 200 - 20, DeliveryTruck t => 200,

Combinazione di tutte le entità

L'esempio seguente evidenzia i vantaggi del pattern matching: i rami del pattern vengono compilati in ordine. Il compilatore avvisa anche del codice non raggiungibile.


 public static int OccupancyTypeTollFare(Object vehicleType) => vehicleType switch { Car { PassengerCount: 0 } => 100 + 10, Car { PassengerCount: 1 } => 100, Car { PassengerCount: 2 } => 100 - 10, Car c => 100 - 20, Taxi { Fare: 0 } => 100 + 10, Taxi { Fare: 1 } => 100, Taxi { Fare: 2 } => 100 - 10, Taxi t => 100 - 20, Bus b when ((double)b.RidersCount / (double)b.Capacity) < 0.50 => 150 + 30, Bus b when ((double)b.RidersCount / (double)b.Capacity) > 0.90 => 150 - 40, Bus b => 150, DeliveryTruck t when (t.Weight > 5000) => 200 + 100, DeliveryTruck t when (t.Weight < 3000) => 200 - 20, DeliveryTruck t => 200, null => 0, { } => 0, };

Prova il programma sopra

Esempi di test dal punto di vista di un'applicazione console. Il codice seguente illustra come chiamare la funzione di pattern-matching di cui sopra dal metodo principale.


 var car1 = new Car{ PassengerCount=2}; var taxi1 = new Taxi { Fare = 0 }; var bus1 = new Bus { Capacity = 100, RidersCount = 30 }; var truck1 = new DeliveryTruck { Weight = 30000 }; Console.WriteLine($"The toll for a car is {OccupancyTypeTollFare(car1)}"); Console.WriteLine($"The toll for a taxi is {OccupancyTypeTollFare(taxi1)}"); Console.WriteLine($"The toll for a bus is {OccupancyTypeTollFare(bus1)}"); Console.WriteLine($"The toll for a truck is {OccupancyTypeTollFare(truck1)}");

Uscita della console

 The toll for a car is 90 The toll for a taxi is 110 The toll for a bus is 180 The toll for a truck is 300


"Il pattern matching rende il codice più leggibile e offre un'alternativa alle tecniche orientate agli oggetti quando non è possibile aggiungere codice alle classi."

Modello di progettazione — Singleton

Il modello di progettazione Gang of Four — Singleton garantisce che una particolare classe abbia una sola istanza/oggetto e un punto di accesso globale.


Obiettivi di apprendimento

  • Come scrivere codice utilizzando un modello di progettazione singleton?

Iniziare

Le classi singleton vengono utilizzate per eliminare l'istanza di più oggetti di una particolare classe.


 public class SingletonExample { private string Name { get; set; } = "Hello from singleton"; private static SingletonExample _instance; public static SingletonExample Instance { get { if (_instance == null) { _instance = new SingletonExample(); } return _instance; } } public SingletonExample() { } public string GetName() => Name; }

Guasto

  1. Iterazione 1 _instance==null significa che verranno create solo istanze.
  2. Iterazione 2, ora _intance !=null Quindi verranno restituite le istanze create in precedenza.

Test tramite un'applicazione console

Chiamiamo la classe singleton due volte e assegniamo l'istanza restituita a due variabili diverse. Infine, controlliamo se entrambi gli oggetti sono uguali usando la funzione Object.Equals.


 static void Main(string[] args) { var response = SingletonExample.Instance; Console.WriteLine(response); var response1 = SingletonExample.Instance; Console.WriteLine(response1); Console.WriteLine(Object.Equals(response1, response)); }
  • Se restituisce true, significa che ogni volta viene prodotta una singola istanza.
  • Se restituisce false, significa che la classe non segue il modello singleton.

Produzione

L'output della console restituisce true; congratulazioni. Hai implementato con successo il Singleton Pattern.



Sicurezza del filo

La classe di cui sopra è nota come classe singleton, ma al momento non è thread-safe. In un ambiente multi-thread, due thread possono raggiungere l'istruzione if (_instance == null) contemporaneamente e finiremo per avere più istanze di una classe singleton.


Un modo per rendere più sicuro un thread è quello di utilizzare un meccanismo di blocco, mentre l'altro modo è quello di creare un'istanza di sola lettura per un approccio più pulito ed efficiente.

 public class ThreadSafeSingleton { private static readonly ThreadSafeSingleton _instance = new ThreadSafeSingleton(); public static ThreadSafeSingleton Instance { get { return _instance; } } public ThreadSafeSingleton() { } }

Esempio di Github

https://github.com/ssukhpinder/DesignPatterns

Grazie per aver letto!

Le sponsorizzazioni mi aiutano a continuare a mantenere e realizzare nuovi progetti come questi.


🙏 Se usi Pay, Noticed o uno qualsiasi dei miei altri progetti, un piccolo contributo significherebbe MOLTO. Da solo, l'open source non paga le bollette. Spero che, con il tuo aiuto, continuare il mio lavoro possa essere sostenibile e non dovrò andare a cercarmi un vero lavoro 😛.

Programmazione C#🚀

Grazie per essere parte della community C#!

Acquista il mio caffè