Deblocați secretele arhitecturii software cu Mastering Software Architecture: 11 Key Design Patterns Explained .
2. Model de proiectare — Adaptor
3. Design Pattern — Builder
4. Cum să utilizați modelul de lanț de responsabilitate
5. Model de design — Decorator
6. Model de proiectare — Metoda fabricii
7. Model de proiectare — Iterator
8. Design Pattern — Mediator
9. Model de design — Observator
10. Advance Property Pattern C# 8.0
11. Model de design — Singleton
Potrivit Gang of Four, modelele abstracte ale fabricii pot fi presupuse ca fiind fabrica pentru crearea fabricilor.
Modelul abstract de fabrică este pur o metodă de fabrică de extensie; se recomandă să treceți prin metoda fabricii înainte de a înțelege designul abstract al fabricii.
Să luăm în considerare același exemplu de orice bancă cu tipuri de conturi, cum ar fi conturi de economii și conturi curente. Acum, să implementăm exemplul de mai sus folosind modelul abstract de design din fabrică.
În primul rând, implementați interfețele ISavingAccount și ICurrentAccount după cum urmează:
public interface ISavingAccount{ } public interface ICurrentAccount{ }
Moșteniți interfața în clasele de mai jos
public class CurrentAccount : ICurrentAccount { public CurrentAccount(string message) { Console.WriteLine(message); } } public class SavingsAccount : ISavingAccount { public SavingsAccount( string message) { Console.WriteLine(message); } }
Să scriem o clasă abstractă cu metode abstracte pentru fiecare tip de cont.
public abstract class AccountTypeFactory { public abstract ISavingAccount SavingAccountFactory(string message); public abstract ICurrentAccount CurrentAccountFactory(string message); }
Acum, să creăm o implementare din fabrică numită „Bank1Factory”, care oferă implementarea metodelor abstracte.
public class Bank1Factory : AccountTypeFactory { public override ICurrentAccount CurrentAccountFactory(string message) { return new CurrentAccount(message); } public override ISavingAccount SavingAccountFactory(string message) { return new SavingsAccount(message); } }
Modelul abstract de proiectare a fabricii diferă de metoda din fabrică de care are nevoie pentru a implementa un furnizor de fabrică, care returnează fabrici conform definiției.
Acum că avem toate abstracțiile și fabricile create. Să proiectăm furnizorul fabricii. Vă rugăm să găsiți mai jos fragmentul de cod pentru furnizorul fabricii, unde o metodă statică va crea o fabrică pe baza numelui contului.
public class AccountFactoryProvider { public static AccountTypeFactory GetAccountTypeFactory(string accountName) { if (accountName.Contains("B1")) { return new Bank1Factory(); } else return null; } }
Să luăm un exemplu de listă de numere de cont în care, dacă un nume de cont constă literalmente din „ B1 ”, atunci va folosi instanța Bank1Factory returnată prin furnizorul din fabrică.
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(); }
Dacă numele contului nu conține literalul „B1”, atunci programul va scoate un {{accountName}} nevalid
Vă rugăm să găsiți mai jos rezultatul din fragmentul de cod de mai sus.
Hello saving B1-456 Hello Current B1-456 Hello saving B1-987 Hello Current B1-987
Potrivit Gang of Four, Adapter Pattern convertește interfețele unei clase în interfețe pe care clientul le cere.
Cu alte cuvinte, modelul de design al adaptorului ajută interfețele incompatibile să funcționeze colectiv.
Să luăm în considerare un exemplu de fuziune a două organizații; Organizația X preia Y, dar în timp ce combină codul, interfețele nu sunt compatibile. Să presupunem că interfața care oferă o listă de tranzacții ale organizației Y nu este compatibilă cu X.
Deci, modelul de design al adaptorului ajută la rezolvarea acestei probleme a cărei implementare este foarte simplă.
Să creăm o listă de tranzacții din organizația Y care sunt convertite în modele pe care le solicită aplicația client a organizației X. Clasa de mai sus este cunoscută ca „Adaptat”.
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; } }
În al doilea rând, să creăm o interfață țintă.
public interface ITransactions{ List<string> GetTransactions(); }
Acum, în sfârșit, să implementăm clasa adaptorului după cum urmează.
public class TransAdapter : OrgYTransactions, ITransactions { public List<string> GetTransactions() { return GetTransactionsList(); } }
După ce toate implementările de mai sus au fost realizate, să înțelegem cum să folosim clasa adaptorului într-o aplicație de consolă.
class Program { static void Main(string[] args) { ITransactions adapter = new TransAdapter(); foreach (var item in adapter.GetTransactions()) { Console.WriteLine(item); } } }
Dacă vă uitați îndeaproape la utilizarea de mai jos, am folosit interfața țintă ITransactions și clasa adaptorului TransAdapter fără a lua în considerare modul în care arată interfețele din clasa terță parte OrgYTransactions. Aceasta este puterea modelului de design al adaptorului, care convertește interfețele unei clase în interfețe pe care le solicită clientul.
Potrivit Gang of Four, un model de creație „Builder” permite separarea și refolosirea unei anumite metode pentru a construi ceva.
Să luăm un exemplu de mașină, iar utilizatorul a dorit să construiască două modele, adică SUV și Sedan.
Modelul de design Builder este util în cazul de utilizare de mai sus și haideți să vedem o demonstrație pas cu pas.
Clasa Car are următoarele proprietăți.
public class Car{ public string Name { get; set; } public double TopSpeed { get; set; } public bool IsSUV { get; set; } }
În primul rând, să implementăm un constructor de clasă abstractă extins cu diferite modele de mașini, cum ar fi SUV-uri sau sedanuri, conform cazului de utilizare.
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; }
Clasa abstractă constă din următoarele metode
Acum, să creăm o fabrică care utilizează clasa CarBuilder pentru a construi diferite modele de mașini și returnează instanța mașinii realizate.
public class CarFactory { public Car Build(CarBuilder builder) { builder.SetName(); builder.SetSpeed(); builder.SetIsSUV(); return builder.GetCar(); } }
În cele din urmă, implementați diferite modele de mașini.
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; } }
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; } }
În cele din urmă, să folosim modele de design pentru a construi diferite modele de mașini cu ajutorul unei metode fabrică.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"); } }
Utilizarea de mai sus arată cât de grațios putem construi diferite modele de mașini folosind modelul de design constructor.
Modelul de cod este foarte ușor de întreținut și extensibil. Dacă, în viitor, trebuie să dezvoltăm un nou model, doar noul model trebuie să extindă clasa CarBuilder și este gata.
Potrivit Gang of Four, definește un lanț de responsabilități pentru procesarea unei cereri. Cu alte cuvinte, treceți cererea de la un obiect la altul până când un obiect își acceptă responsabilitatea.
Să luăm în considerare un exemplu de sistem de daune în orice companie corporativă. Iată lista intervalului de preț care poate fi aprobat și de către cine.
100–1000 Rs => Junior/Senior Engineers => Approved by Manager 1001–10000 Rs => Managers => Approved by Senior Manager
Dacă suma este în afara intervalului de 10000, este necesară aprobarea excepțională din partea managerului superior.
Cazul de utilizare de mai sus poate fi implementat cu ușurință folosind modelul de proiectare Lanț de responsabilitate. Deci, clasa de revendicare are următoarele proprietăți.
public class Claim{ public int Id{get;set;} public double amount{get;set;} }
În primul rând, să definim ce funcții poate îndeplini un autorizator de revendicare și să stabilim o ierarhie pentru angajații de la diferite niveluri. Implementați o clasă abstractă, așa cum se arată mai jos
public abstract class ClaimApprover { protected ClaimApprover claimApprover; public void SetHierarchy(ClaimApprover claimApprover) { this.claimApprover = claimApprover; } public abstract void ApproveRequest(Claim claim); }
Conform cazului de utilizare, haideți să conducem solicitantul de revendicare din clasa „junior/senior”. Observați că această clasă/desemnare de angajați nu poate aproba nicio revendicare.
public class Junior : ClaimApprover { public override void ApproveRequest(Claim claim) { System.Console.WriteLine("Cannot approve"); } }
În mod similar, să definim implementarea pentru rolurile de Manager și 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); } } }
Observați că pe baza intervalului de sume, dacă se află în intervalul Managerului, cererea poate fi aprobată de către Manager; în caz contrar, cererea va fi transmisă Senior Managerului.
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"); } } }
În mod similar, dacă intervalul de sume se află în intervalul Senior Manager, cererea poate fi aprobată de către Manager; în caz contrar, fiind ultimul în ierarhie, se face o aprobare excepțională pentru o sumă în afara intervalului.
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);
Claim reference 1001 with amount 999 is approved by Manager Exceptional approval for Claim reference 1002 with amount 10001 is approved by Senior Manager
Pentru ieșirea liniei 1, suma a fost în interval, așa că managerul a aprobat-o.
Pentru ieșirea din linia 2, deși managerul superior a aprobat-o, suma a fost în afara intervalului.
Potrivit Gang of Four, modelul adaugă responsabilități suplimentare unui obiect de clasă în mod dinamic.
Să luăm în considerare exemplul cumpărării unei mașini în valoare de zece lakhs; compania oferă următoarele caracteristici suplimentare.
Cu unele caracteristici suplimentare, prețul total al mașinii crește. Să implementăm cazul de utilizare de mai sus folosind modelul Decorator.
Să implementăm cazul de utilizare definit mai sus. În primul rând, definiți o clasă abstractă Car și metodele sale de bază.
public abstract class Car{ public abstract int CarPrice(); public abstract string GetName(); }
Luați în considerare o mașină mică care se extinde deasupra clasei abstracte Car.
public class SmallCar : Car{ public override int CarPrice() => 10000; public override string GetName() => "Alto Lxi"; }
Acum, implementați clasa CarDecorator folosind componenta 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(); }
Acum, să creăm o clasă separată pentru fiecare caracteristică suplimentară disponibilă pentru Car care moștenește clasa CarDecorator.
Conform cazului de utilizare, caracteristicile suplimentare sunt o trapă și un sistem muzical avansat.
Ignorați metodele ca
Adăugați costul suplimentar al unui „sistem muzical avansat” la prețul total al mașinii.
Actualizați numele mașinii cu numele caracteristicii suplimentare.
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"; }
Ignorați metodele ca
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"; }
Creați o instanță de SmallCar și afișați numele și prețul mașinii.
Car car = new SmallCar(); Console.WriteLine($"Price of car {car.GetName()} : " + car.CarPrice());
Acum, să adăugăm funcții suplimentare, așa cum se arată mai jos
var car1 = new Sunroof(car); var car2 = new AdvanceMusic(car);
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()); }
Felicitări..!! Ați implementat cu succes cazul de utilizare folosind modelul decorator.
Conform Gang of Four, metoda din fabrică permite subclasei să determine ce obiect de clasă trebuie creat.
Să luăm în considerare un exemplu de orice bancă cu tipuri de conturi ca de economii și conturi curente. Acum, să implementăm exemplul de mai sus folosind modelul de proiectare din fabrică
În primul rând, creați o clasă abstractă de tip cont.
public abstract class AccoutType { public string Balance { get; set; } }
Implementați clasele de cont curente și de salvare moștenind clasa abstractă AccountType, așa cum se arată mai jos.
public class SavingsAccount : AccoutType { public SavingsAccount() { Balance = "10000 Rs"; } } public class CurrentAccount : AccoutType { public CurrentAccount() { Balance = "20000 Rs"; } }
În cele din urmă, să implementăm interfața din fabrică, care va oferi un contract care ajută la crearea unui obiect de clasă. Această interfață este cunoscută și sub numele de Creator.
public interface IAccountFactory { AccoutType GetAccoutType(string accountName); }
În cele din urmă, scrieți o implementare a metodei interfeței creatorului, așa cum se arată mai jos. Clasa care implementează creatorul este cunoscută sub numele de Creator de beton.
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"); } } }
Asta este. Ați implementat cu succes metoda din fabrică folosind exemplul Bank.
O subclasă va decide ce obiect de clasă „AccountType” va fi creat pe baza numelui contului.
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); } }
De exemplu, dacă numele contului este „SAVINGS”, atunci obiectul clasei „SavingAccount” va fi creat și returnat.
În mod similar, dacă numele contului este „CURRENT”, atunci obiectul clasei „CurrentAccount” va fi instanțiat și returnat.
Saving account balance: 10000 Rs Current account balance: 20000 Rs
Potrivit Gang of Four, modelul iterator oferă un proces pentru a obține obiectul agregator fără a cunoaște implementarea acestuia.
Să luăm un exemplu de listă de colecție de mașini și string[] o matrice de motociclete, trebuie să proiectăm un obiect agregator astfel încât să se poată itera peste colecție fără a ști dacă este o listă sau o matrice.
Modelul de proiectare a iteratorului ajută la rezolvarea acestei probleme în care un iterator standard va traversa diferite tipuri de colecție.
Având în vedere cazul de utilizare de mai sus, să definim o interfață de iterator personalizată care acționează ca un strat abstract peste iteratorul de listă și matrice.
public interface IVehicleIterator{ void First(); bool IsDone(); string Next(); string Current(); }
Acum, scrieți iteratoare pentru mașini și motociclete care implementează interfața de mai sus în funcție de cazul de utilizare.
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++); } }
Iteratorul auto este implementat peste colecția List<string> și oferă o implementare a metodelor de interfață.
Iteratorul motocicletei este implementat peste colecția string[] și oferă o implementare a metodelor de interfață.
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++]; } }
După ce toți iteratorii de mai sus sunt definiți, definiți o interfață standard de obiect agregator care creează iteratoare.
public interface IVehicleAggregate{ IVehicleIterator CreateIterator(); }
În cele din urmă, notați clasele care implementează interfața de agregare de mai sus. Conform cazului de utilizare, ambele clase de mașini și motociclete vor implementa interfața de agregare.
Metoda interfeței agregatorului returnează iteratorul relevant, așa cum se arată mai jos.
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); } }
Metoda interfeței agregatorului returnează iteratorul relevant, așa cum se arată mai jos.
public class Motercycle : IVehicleAggregate { private string[] _motercycles; public Motercycle() { _motercycles = new[] { "Bike 1", "Bike 2", "Bike 3" }; } public IVehicleIterator CreateIterator() { return new MotercycleIterator(_motercycles); } }
Metodele PrintVehicles verifică dacă !iterator.isDone apoi scoate elementul de colecție. Indiferent de ce colecție avem de-a face, implementați metode precum First, IsDone și 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()); } }
Nu cunoaștem tipul de colecție de bază, dar este încă repetat prin modelul de proiectare Iterator. Dacă mergeți mai departe și rulați, afișează următoarea ieșire.
Potrivit Gang of Four, modelul Mediator încapsulează interacțiunea obiectului unul cu celălalt.
Modelul de design al mediatorului ne ajută să proiectăm aplicații slab cuplate prin încapsularea interacțiunilor obiectelor.
Să luăm în considerare un exemplu de cameră de chat în care participanții se înregistrează și cum să comunice eficient.
Trebuie să implementați următoarea conversație de chat folosind modelul de design Mediator.
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?'
Pasul principal este de a crea o listă de nume de utilizator care vor fi folosite în interiorul unei camere de chat. O enumerare publică pentru aceasta este afișată mai jos.
public enum Username{ Ashley, David, Jennifer, Scott }
Acum, în primul rând, implementați un strat abstract al camerei de chat.
public abstract class AChatroom { public abstract void Register(User user); public abstract void Post(string fromUser, string toUser, string msg); }
Și o clasă care definește metode abstracte. Metodele validează dacă utilizatorul există în dicționar. De exemplu, metoda de înregistrare validează dacă utilizatorul există deja sau nu. Dacă nu există, atunci înregistrați utilizatorul doar în camera de chat.
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; } }
În cele din urmă, să implementăm acțiunile pe care utilizatorul le poate efectua, cum ar fi postarea unui mesaj către un utilizator în camera de chat sau primirea unui DM de la alt utilizator.
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); }
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(); }
Execuția programului descrie doar metoda Post a clasei de utilizator.
Ieșire: Istoricul camerei de chat al execuției programului de mai sus
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?'
Conform Gang of Four, modelul observatorului definește dependența între două sau mai multe obiecte. Deci, atunci când starea unui obiect se schimbă, atunci toți dependenții lui sunt notificați.
Cu alte cuvinte, o modificare a unui obiect inițiază notificarea într-un alt obiect.
Să luăm un exemplu de influență de celebritate Instagram care are un număr „ x ” de urmăritori. Deci, în momentul în care celebritatea adaugă o postare, atunci toți urmăritorii sunt anunțați.
Să implementăm cazul de utilizare menționat anterior folosind modelul de proiectare Observer.
Conform cazului de utilizare, primul implementează o interfață care conține ce acțiuni poate efectua o celebritate. Este cunoscut ca „ Subiect ”.
public interface ICelebrityInstagram{ string FullName { get; } string Post { get; set; } void Notify(string post); void AddFollower(IFollower fan); void RemoveFollower(IFollower fan); }
Notificare: Pentru a notifica toți adepții.
AddFollower: Adăugați un nou urmăritor la lista de celebrități.
RemoveFollower: Eliminați un urmăritor din lista de celebrități.
Acum, implementați interfața de observator „IFollower”, care conține funcția de membru „Actualizare” pentru notificare.
public interface IFollower{ void Update(ICelebrityInstagram celebrityInstagram); }
În cele din urmă, este timpul să implementăm „Implementare concretă” atât pentru „ Subiect ”, cât și pentru „ Observator ”.
Oferă o implementare a funcției de membru Actualizare, care trimite numele celebrității și postarea pe consolă.
public class Follower : IFollower { public void Update(ICelebrityInstagram celebrityInstagram) { Console.WriteLine($"Follower notified. Post of {celebrityInstagram.FullName}: " + $"{celebrityInstagram.Post}"); } }
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); } }
Următorul caz de utilizare arată că ori de câte ori instrucțiunea de mai jos este executatăsukhpinder.Post = „Îmi plac modelele de design.”; Metoda de actualizare este declanșată pentru fiecare urmăritor, adică fiecare obiect de urmăritor este notificat cu privire la o nouă postare de la „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(); }
Articolul descrie modul în care potrivirea modelelor oferă o modalitate eficientă de utilizare și procesare a acestor date în formulare care nu făceau parte din sistemul primar.
Să luăm un exemplu de Calculator de taxă și să vedem cum potrivirea modelelor ajută la scrierea unui algoritm pentru asta.
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; } }
Exemplul 1: Calculați tariful de taxare conform următoarelor condiții:
- Dacă vehiculul este mașină => 100 Rs
- Dacă vehiculul este DeliveryTruck => 200 Rs
- Dacă vehiculul este Autobuz => 150 Rs
- Dacă vehiculul este un taxi => 120 Rs
Dacă tipul de vehicul se potrivește cu Car 100 este returnat și așa mai departe. Observați că null și {} sunt cazuri implicite pentru tipul de obiect.
De asemenea, „_” poate fi folosit pentru a programa scenariul implicit. Consultați noua sintaxă a comutatorului.
Este o modalitate mult mai curată și mai eficientă de codare și, de asemenea, a recomandat utilizarea numelor de variabile cu o singură literă în sintaxa comutatorului.
public static int TollFare(Object vehicleType) => vehicleType switch { Car c => 100, DeliveryTruck d => 200, Bus b => 150, Taxi t => 120, null => 0, { } => 0 };
Exemple de testare din punct de vedere al aplicației consolă. Codul de mai jos ilustrează cum să apelați funcția de potrivire a modelelor de mai sus din metoda principală.
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)}");
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
Exemplul 2: adăugați prețul de ocupare în funcție de tipul de vehicul
- Mașinile și taxiurile cu pasageri „NU” plătesc 10 Rs.
- Mașinile și taxiurile cu doi pasageri beneficiază de o reducere de 10 lei.
- Mașinile și taxiurile cu trei sau mai mulți pasageri beneficiază de o reducere de 20 de lei.
- Autobuzele care reprezintă mai puțin de 50% din pasageri plătesc 30 de lei în plus.
- Autobuzele care au mai mult de 90% din pasageri beneficiază de o reducere de 40 de lei.
- Camioanele de peste 5000 lbs sunt taxate cu 100 Rs.
- Camioane ușoare sub 3000 lbs, cu o reducere de 20 Rs.
Consultați sintaxa de potrivire a modelelor cu clase de proprietăți unice și multiple. Legătură
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,
Exemplul de mai jos evidențiază avantajele potrivirii modelelor: ramurile modelului sunt compilate în ordine. Compilatorul avertizează și despre codul inaccesibil.
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, };
Exemple de testare din punct de vedere al aplicației consolă. Codul de mai jos ilustrează cum să apelați funcția de potrivire a modelelor de mai sus din metoda principală.
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)}");
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
„Potrivirea modelelor face codul mai ușor de citit și oferă o alternativă la tehnicile orientate pe obiecte atunci când nu puteți adăuga cod la cursuri.”
Gang of Four — Modelul de design Singleton asigură că o anumită clasă are o singură instanță/obiect și un punct de acces global.
Clasele Singleton sunt folosite pentru a elimina instanțiarea mai multor obiecte dintr-o anumită clasă.
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; }
Să apelăm de două ori clasa singleton și să atribuim instanța returnată la două variabile diferite. În cele din urmă, verificați dacă ambele obiecte sunt egale folosind funcțiaObject.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)); }
Ieșirea consolei returnează true; Felicitări. Ați implementat cu succes modelul Singleton.
Clasa de mai sus este cunoscută ca clasa singleton, dar în prezent, nu este sigură pentru fire. Într-un mediu cu mai multe fire, două fire pot atinge instrucțiunea if (_instance == null) în același timp și vom ajunge să avem mai multe instanțe ale unei clase singleton.
O modalitate pentru un fir mai sigur este folosirea unui mecanism de blocare, iar cealaltă modalitate este de a crea o instanță de numai citire pentru o abordare mai curată și mai eficientă.
public class ThreadSafeSingleton { private static readonly ThreadSafeSingleton _instance = new ThreadSafeSingleton(); public static ThreadSafeSingleton Instance { get { return _instance; } } public ThreadSafeSingleton() { } }
https://github.com/ssukhpinder/DesignPatterns
Sponsorizările mă ajută să continui să mențin și să construiesc noi proiecte ca acestea.
🙏 Dacă utilizați Pay, Noticed sau oricare dintre celelalte proiecte ale mele, o mică contribuție ar însemna MULT. Pe cont propriu, open-source nu plătește facturile. Sper că, cu ajutorul tău, să-mi continui munca poate fi sustenabilă și nu va trebui să merg să-mi iau un loc de muncă adevărat 😛.
Vă mulțumim că faceți parte din comunitatea C#!