paint-brush
11 patróns de deseño clave: unha guía esencialpor@ssukhpinder
Nova historia

11 patróns de deseño clave: unha guía esencial

por Sukhpinder Singh33m2024/09/08
Read on Terminal Reader

Demasiado longo; Ler

Descubra os segredos da arquitectura de software con *Mastering Software Architecture: 11 Key Design Patterns Explained*. O libro está escrito na linguaxe de programación C# C# 8.0. Hai 11 patróns de deseño que se poden usar para crear unha fábrica para crear fábricas. O patrón de fábrica abstracto é un método de fábrica de extensión; recoméndase pasar polo método de fábrica.
featured image - 11 patróns de deseño clave: unha guía esencial
Sukhpinder Singh HackerNoon profile picture
0-item
1-item
2-item
3-item
4-item

Desbloquea os segredos da arquitectura de software con Mastering Software Architecture: 11 Key Design Patterns Explained .

Índice

  1. Patrón de deseño — Abstract Factory
  • Obxectivos de aprendizaxe
  • Comezando
  • Como usar un provedor de fábrica abstracto?
  • Saída

2. Patrón de deseño — Adaptador

  • Caso de uso
  • Obxectivos de aprendizaxe
  • Comezando

3. Patrón de deseño — Construtor

  • Caso de uso
  • Obxectivos de aprendizaxe
  • Comezando
  • Como usar o patrón do constructor do método Main().
  • Saída

4. Como utilizar o Patrón da cadea de responsabilidade

  • Caso de uso
  • Comezando
  • Como usar o patrón da cadea de responsabilidade?
  • Saída

5. Patrón de deseño — Decorador

  • Caso de uso
  • Obxectivos de aprendizaxe
  • Comezando
  • Patrón de decorador en acción
  • Código completo
  • Saída

6. Patrón de deseño — Método de fábrica

  • Obxectivos de aprendizaxe
  • Comezando
  • Como usar o método de fábrica?
  • Saída

7. Patrón de deseño — Iterador

  • Caso de uso
  • Comezando
  • Patrón de iterador en acción
  • Saída

8. Patrón de deseño — Mediador

  • Caso de uso
  • Obxectivos de aprendizaxe
  • Comezando
  • Como usar o patrón mediador do método principal

9. Patrón de deseño — Observador

  • Caso de uso
  • Obxectivos de aprendizaxe
  • Comezando
  • Como usar un patrón de observador?
  • Saída

10. Patrón de propiedade avanzada C# 8.0

  • Comezamos
  • Programa de coincidencia de patróns cunha nova sintaxe de interruptor
  • Programa de proba
  • Saída da consola

11. Patrón de deseño - Singleton

  • Obxectivos de aprendizaxe
  • Comezando
  • Saída
  • Seguridade do fío

Patrón de deseño — Abstract Factory

Segundo Gang of Four, os patróns de fábrica abstractos pódense asumir como a fábrica para crear fábricas.


Obxectivos de aprendizaxe

  • Cal é o patrón abstracto de deseño de fábrica?
  • Como escribir código usando o patrón de deseño de fábrica abstracto?
  • Como crear un provedor de fábrica?
  • Como crear unha aplicación cliente (a partir do método Main) que usa un provedor de fábrica

Requisitos previos

O patrón de fábrica abstracto é puramente un método de fábrica de extensión; recoméndase pasar polo método de fábrica antes de comprender o deseño abstracto de fábrica.

  • Coñecementos básicos dos conceptos OOPS.
  • Calquera coñecemento da linguaxe de programación.

Comezando

Consideremos o mesmo exemplo de calquera banco con tipos de contas como aforros e contas correntes. Agora, imos implementar o exemplo anterior usando o patrón de deseño de fábrica abstracto.


En primeiro lugar, implemente as interfaces ISavingAccount e ICurrentAccount do seguinte xeito:


 public interface ISavingAccount{ } public interface ICurrentAccount{ }


Herda a interface nas clases seguintes


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


Escribamos unha clase abstracta con métodos abstractos para cada tipo de conta.


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


Agora, imos crear unha implementación de fábrica chamada "Bank1Factory", que proporciona a implementación de métodos abstractos.


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


O patrón de deseño de fábrica abstracto difire do método de fábrica que necesita para implementar un provedor de fábrica, que devolve as fábricas segundo a definición.


Agora que temos todas as abstraccións e fábricas creadas. Deseñamos o provedor da fábrica. Busca a continuación o fragmento de código para o provedor de fábrica, onde un método estático creará unha fábrica baseada no nome da conta.


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

Como usar un provedor de fábrica abstracta?

Poñamos un exemplo dunha lista de números de conta onde se un nome de conta consta literalmente de “ B1 ”, entón utilizará a instancia de Bank1Factory devolta a través do provedor de fábrica.

 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 o nome da conta non contén o literal "B1", entón o programa mostrará un {{accountName}} non válido

Saída

Busca a continuación a saída do fragmento de código anterior.


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

Patrón de deseño - Adaptador

Segundo Gang of Four, o patrón do adaptador converte as interfaces dunha clase en interfaces que o cliente require.


Noutras palabras, o patrón de deseño do adaptador axuda a que as interfaces incompatibles funcionen colectivamente.

Caso de uso

Consideremos un exemplo de fusión de dúas organizacións; A organización X está a facerse cargo de Y, pero ao combinar código, as interfaces non son compatibles. Supoña que a interface que proporciona unha lista de transaccións da organización Y non é compatible con X.


Así, o patrón de deseño do adaptador axuda a resolver este problema cuxa implementación é moi sinxela.

Obxectivos de aprendizaxe

  • Como codificar usando un patrón de deseño de adaptador?

Comezando

Imos crear unha lista de transaccións da organización Y que se converten en patróns que require a aplicación cliente da organización X. A clase anterior coñécese como "Adaptado".


 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; } }


En segundo lugar, imos crear unha interface de destino.


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


Agora, finalmente, imos implementar a clase do adaptador do seguinte xeito.


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


Despois de facer todas as implementacións anteriores, imos entender como usar a clase de adaptador nunha aplicación de consola.


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


Se observas atentamente o seguinte uso, utilizamos a interface de destino ITransactions e a clase de adaptador TransAdapter sen ter en conta o aspecto das interfaces de clase OrgYTransactions de terceiros. Ese é o poder do patrón de deseño do adaptador que converte as interfaces dunha clase en interfaces que o cliente require.

Patrón de deseño - Construtor

Segundo Gang of Four, un patrón de creación "Builder" permite separar e reutilizar un método específico para construír algo.


Caso de uso

Poñamos un exemplo dun Coche, e o usuario quería construír dous modelos, é dicir, SUV e Sedan.


O patrón de deseño do constructor é útil no caso de uso anterior e vexamos unha demostración paso a paso.


A clase Car ten as seguintes propiedades.

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

Obxectivos de aprendizaxe

  • Como codificar usando un patrón de deseño de constructor?

Comezando

En primeiro lugar, imos implementar un constructor de clases abstracto estendido por diferentes modelos de coches como SUV ou sedans segundo o caso de 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; }


A clase abstracta consta dos seguintes métodos

  • Métodos abstractos para cada propiedade da clase Car.
  • Un método virtual que produce a instancia da clase Car.


Agora, imos crear unha fábrica que utilice a clase CarBuilder para construír diferentes modelos de coches e que devolva a instancia do coche feito.


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


Por último, implementar diferentes modelos de coches.

ModeloSuv.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; } }

ModeloSedan.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; } }

Como crear un patrón de usuario desde o método Main().

Finalmente, usemos patróns de deseño para construír diferentes modelos de coche coa axuda dun método 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"); } }

O uso anterior mostra a gracia que podemos construír diferentes modelos de coches usando o patrón de deseño do constructor.


O patrón de código é altamente mantenible e extensible. Se, no futuro, necesitamos desenvolver un novo modelo, só o novo modelo necesita estender a clase CarBuilder, e xa está.

Saída

Como usar o patrón da cadea de responsabilidade

Segundo Gang of Four, define unha cadea de responsabilidades para procesar unha solicitude. Noutras palabras, pasar a solicitude dun obxecto a outro ata que un obxecto acepte a súa responsabilidade.


Caso de uso

Consideremos un exemplo de sistema de reclamacións en calquera empresa corporativa. Aquí está a lista do rango de prezos que se pode aprobar e por quen.


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


Se o importe está fóra do rango de 10000, é necesaria a aprobación excepcional do director superior.


O caso de uso anterior pódese implementar facilmente mediante o patrón de deseño da cadea de responsabilidade. Polo tanto, a clase de reivindicación ten as seguintes propiedades.


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

Comezando

En primeiro lugar, imos definir que funcións pode realizar un aprobador de reclamacións e establecer unha xerarquía para os empregados de diferentes niveis. Implementa unha clase abstracta como se mostra a continuación


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


Segundo o caso de uso, imos dirixir o solicitante de reclamación de clase "junior/senior". Teña en conta que esta clase/designación de empregados non pode aprobar ningunha reclamación.


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


Do mesmo xeito, imos definir a implementación para os roles de xestor e xestor superior.


 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); } } }


Teña en conta que en función do intervalo de cantidades, se está dentro do intervalo do xestor, a reclamación pode ser aprobada polo xestor; en caso contrario, a solicitude será trasladada ao Director Superior.


 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"); } } }


Do mesmo xeito, se o intervalo de cantidades está dentro do rango do xestor superior, a reclamación pode ser aprobada polo xerente; en caso contrario, sendo o último da xerarquía, faise unha aprobación excepcional por un importe fóra do rango.


 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);

Como usar o patrón da cadea de responsabilidade?

  1. Defina aprobador de reclamacións: junior, aínda que non pode aprobar ningunha reclamación.
  2. Defina o aprobador de reclamacións: xestor "sukhpinder".
  3. Define o aprobador de reclamacións: xerente superior "Singh".
  4. Establece unha relación de xerarquía para junior, é dicir, o aprobador de reclamacións é o xestor.
  5. Establece unha relación de xerarquía para o xestor, é dicir, o aprobador de reclamacións é o director superior.
  6. Crea dous rangos diferentes de reclamacións.
  7. Junior envía a solicitude de reclamación ao xestor.
  8. O xestor envía a solicitude de reclamación ao xestor superior.

Saída

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


Para a saída da liña 1, o importe estaba dentro do intervalo, polo que o xestor aprobouno.


Para a saída da liña 2, aínda que o xestor superior o aprobou, a cantidade estaba fóra do rango.

Patrón de deseño - Decorador

Segundo Gang of Four, o patrón engade responsabilidades adicionais a un obxecto de clase de forma dinámica.


Caso de uso

Consideremos o exemplo de mercar un coche que vale dez lakhs; a empresa ofrece as seguintes funcións adicionais.

  • Teito solar
  • Sistema de música avanzado
  • e moitos máis


Con algunhas características adicionais, o prezo total do coche aumenta. Imos implementar o caso de uso anterior usando o Patrón Decorador.

Obxectivos de aprendizaxe

  • Como codificar usando un patrón de deseño de decorador?

Comezando

Imos implementar o caso de uso definido anteriormente. En primeiro lugar, define unha clase abstracta Car e os seus métodos base.


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


Considere un coche pequeno que se estende por riba da clase abstracta Car.


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


Agora, implementa a clase CarDecorator usando o compoñente 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(); }


Agora, imos crear unha clase separada para cada función adicional dispoñible para Car que herda a clase CarDecorator.


Segundo o caso de uso, as características adicionais son un teito solar e un sistema de música avanzado.

AdvanceMusic.cs

Anular os métodos como

  • Engade o custo adicional dun "sistema de música avanzado" ao prezo total do coche.

  • Actualiza o nome do coche cun nome de función adicional.

     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"; }

Teito solar. cs

Anular os métodos como

  • Engade o custo adicional dun "teito solar" ao prezo total do coche.
  • Actualiza o nome do coche cun nome de función adicional.
 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"; }

Patrón de decorador en acción

Crea unha instancia de SmallCar e mostra o nome e o prezo do coche.


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


Agora, imos engadir funcións adicionais como se mostra a continuación


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

Código 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()); }

Saída

Parabéns..!! Implementou correctamente o caso de uso usando o patrón de decorador.

Patrón de deseño - Método de fábrica

Segundo a Gang of Four, o método de fábrica permite que a subclase determine que obxecto de clase debe crearse.


Obxectivos de aprendizaxe

  • Cal é o patrón de deseño do método de fábrica?
  • Como escribir código usando o método de fábrica?

Comezando

Consideremos un exemplo de calquera banco con tipos de conta como Aforro e Conta corrente. Agora, imos implementar o exemplo anterior usando o patrón de deseño de fábrica


En primeiro lugar, cree unha clase abstracta tipo conta.


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


Implementa clases de conta actuais e de aforro herdando a clase abstracta AccountType como se mostra a continuación.


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


Finalmente, imos implementar a interface de fábrica, que proporcionará un contrato que axuda a crear un obxecto de clase. Esta interface tamén se coñece como o Creador.


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


Por último, escribe unha implementación do método de interface de creador como se mostra a continuación. A clase que implementa o creador coñécese como 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"); } } }


Iso é. Implementou correctamente o método de fábrica usando o exemplo de Banco.

Como usar o método de fábrica?

Unha subclase decidirá que obxecto de clase "AccountType" se creará en función do nome da conta.


 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); } }

Por exemplo, se o nome da conta é "SAVINGS", crearase e devolverase o obxecto da clase "SavingAccount".


Do mesmo xeito, se o nome da conta é "CURRENT", entón o obxecto da clase "CurrentAccount" será instanciado e devolto.

Saída

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

Patrón de deseño — Iterador

Segundo Gang of Four, o patrón iterador proporciona un proceso para obter o obxecto agregador sen coñecer a súa implementación.


Caso de uso

Poñamos un exemplo dunha lista de colección de coches e string[] unha matriz de motocicletas, necesitamos deseñar un obxecto agregador para que se poida iterar sobre a colección sen saber se é unha lista ou unha matriz.


O patrón de deseño do iterador axuda a resolver este problema no que un iterador estándar atravesará diferentes tipos de colección.

Comezando

Tendo en conta o caso de uso anterior, imos definir unha interface de iterador personalizada que actúe como unha capa abstracta sobre o iterador de lista e matriz.


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


Agora, escribe iteradores de coches e motocicletas que implementen a interface anterior segundo o caso de 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++); } }


O iterador de coches está implementado sobre a colección List<string> e proporciona unha implementación de métodos de interface.

MotorcycleIterator.cs

O iterador de motocicletas está implementado sobre a colección string[] e ofrece unha implementación de métodos de interface.


 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++]; } }


Despois de definir todos os iteradores anteriores, defina unha interface de obxecto de agregador estándar que cree iteradores.


 public interface IVehicleAggregate{ IVehicleIterator CreateIterator(); }


Finalmente, anote as clases que implementan a interface do agregador anterior. Segundo o caso de uso, tanto as clases de coche como de motocicleta implementarán a interface de agregación.

Coche. cs

O método da interface do agregador devolve o iterador relevante como se mostra a continuación.


 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); } }

Motocicleta. cs

O método da interface do agregador devolve o iterador relevante como se mostra a continuación.


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

Patrón de iterador en acción

Os métodos PrintVehicles verifican se !iterator.isDone saen entón o elemento de colección. Non importa a colección coa que esteamos a tratar, implementa métodos como 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()); } }

Saída

Non coñecemos o tipo de colección subxacente, pero aínda se repite a través do patrón de deseño do iterador. Se continúa e executa, mostra a seguinte saída.

Patrón de deseño — Mediador

Segundo Gang of Four, o patrón Mediator encapsula a interacción do obxecto entre si.


O patrón de deseño do mediador axúdanos a deseñar aplicacións pouco acopladas ao encapsular as interaccións de obxectos.

Caso de uso

Consideremos un exemplo de sala de chat onde se rexistran os participantes e como comunicarse de forma eficiente.


É necesario implementar a seguinte conversa na sala de chat usando o patrón de deseño do mediador.


 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?'

Obxectivos de aprendizaxe

  • Como codificar usando un patrón de deseño mediador?

Comezando

O paso principal é crear unha lista de nomes de usuario que se utilizarán dentro dunha sala de chat. A continuación móstrase unha enumeración pública para iso.


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


Agora, en primeiro lugar, implementa unha capa abstracta da sala de chat.


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


E unha clase que define métodos abstractos. Os métodos validan se o usuario existe no dicionario. Por exemplo, o método de rexistro valida se o usuario xa existe ou non. Se non existe, só rexistra o usuario na sala 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; } }


Finalmente, imos implementar as accións que pode realizar o usuario, como publicar unha mensaxe a un usuario na sala de chat ou recibir un DM doutro usuario.


 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); }

Como usar o patrón mediador do método principal

 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. Créase o obxecto da clase de chatroom.
  2. Créanse catro usuarios diferentes con nomes únicos.
  3. Rexistra cada un deles na sala de chat.
  4. Agora os usuarios poden comezar a publicar mensaxes entre si.

A execución do programa describe só o método Post da clase de usuario.


Saída: o historial da sala de chat da execución do programa anterior


 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?'

Patrón de deseño — Observer

Segundo Gang of Four, o patrón do observador define a dependencia b/w de dous ou máis obxectos. Entón, cando o estado dun obxecto cambia, todos os seus dependentes son notificados.


Noutras palabras, un cambio nun obxecto inicia a notificación noutro obxecto.

Caso de uso

Poñamos un exemplo dun influencer famoso de Instagram que ten " x " número de seguidores. Entón, no momento en que a celebridade engade unha publicación, todos os seguidores son notificados.


Imos implementar o caso de uso mencionado anteriormente usando o patrón de deseño de Observer.

Obxectivos de aprendizaxe

  • Como codificar usando un patrón de deseño de observador?

Comezando

Segundo o caso de uso, o primeiro implementa unha interface que contén que accións pode realizar unha celebridade. Coñécese como " Suxeito ".


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

O asunto contén as seguintes funcións de membro.

  • Notificar: para notificar a todos os seguidores.

  • Engadir seguidor: engade un novo seguidor á lista de famosos.

  • Eliminar seguidor: elimina un seguidor da lista de famosos.


Agora, implementa a interface de observador "IFollower", que contén a función de membro "Actualizar" para a notificación.


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


Finalmente, é hora de implementar a "Implementación concreta" tanto para " Suxeito " como para " Observador ".

ConcreteObserver chamado "Follower.cs"

Proporciona unha implementación da función de membro Actualizar, que mostra o nome e a publicación da celebridade na consola.


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

ConcreteSubject chamado "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); } }

Como crear un patrón de observador?

O seguinte caso de uso mostra que sempre que se executa a seguinte instruciónsukhpinder.Post = "Encántanme os patróns de deseño."; O método de actualización desenvólvese para cada seguidor, é dicir, cada obxecto seguidor recibe unha notificación dunha nova publicación de "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(); }

Saída

Patrón de propiedade avanzada C# 8.0

O artigo describe como a correspondencia de patróns proporciona un xeito eficaz de utilizar e procesar eses datos en formularios que non formaban parte do sistema principal.


Comezamos

Poñamos un exemplo da calculadora de peaxes e vexamos como a coincidencia de patróns axuda a escribir un algoritmo para iso.

Clase de entidade utilizada ao longo do artigo

 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; } }


Exemplo 1: calcula a tarifa de peaxe segundo as seguintes condicións:


  • Se o vehículo é Coche => 100 Rs
  • Se o vehículo é DeliveryTruck => 200 Rs
  • Se o vehículo é Autobús => 150 Rs
  • Se o vehículo é un Taxi => 120 Rs

Programa de coincidencia de patróns con nova sintaxe de interruptor

Se o tipo de vehículo coincide co Car 100 é devolto e así por diante. Teña en conta que nulo e {} son casos predeterminados para o tipo de obxecto.

Ademais, "_" pódese usar para programar o escenario predeterminado. Consulte a nova sintaxe do interruptor.


É unha forma moito máis limpa e eficiente de codificar e tamén recomenda o uso de nomes de variable dunha soa letra dentro da sintaxe do switch.


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

Programa de proba anterior

Exemplos de proba desde o punto de vista da aplicación de consola. O seguinte código ilustra como chamar á función de coincidencia de patróns anterior desde o método 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)}");

Saída da consola

 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


Exemplo 2: engade o prezo de ocupación en función do tipo de vehículo


  • Os coches e taxis con pasaxeiros "NON" pagan 10 Rs.
  • Os coches e taxis con dous pasaxeiros teñen un desconto de 10 Rs.
  • Os coches e taxis con tres ou máis pasaxeiros reciben un desconto de 20 Rs.
  • Os autobuses que son menos do 50% dos pasaxeiros pagan 30 Rs.
  • Os autobuses que teñen máis do 90% dos pasaxeiros teñen un desconto de 40 Rs.
  • Os camións de máis de 5000 libras cobran 100 Rs.
  • Camións lixeiros de menos de 3000 libras, con desconto de 20 Rs.

Interruptor de coincidencia de patróns

Consulte a sintaxe de coincidencia de patróns con clases de propiedades únicas e múltiples. Ligazón

Correspondencia de patróns — Entidade do coche

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

Correspondencia de patróns — Entidade de taxi

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

Correspondencia de patróns — Entidade de autobús

 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,

Correspondencia de patróns - Entidade de camións de reparto

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

Combinando todas as entidades

O seguinte exemplo destaca as vantaxes da coincidencia de patróns: as ramas de patróns compílanse en orde. O compilador tamén advirte sobre o código inalcanzable.


 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, };

Programa de proba anterior

Exemplos de proba desde o punto de vista da aplicación de consola. O seguinte código ilustra como chamar a función de coincidencia de patróns anterior desde o método 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)}");

Saída da consola

 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


"A coincidencia de patróns fai que o código sexa máis lexible e ofrece unha alternativa ás técnicas orientadas a obxectos cando non podes engadir código ás túas clases".

Patrón de deseño - Singleton

Gang of Four: o patrón de deseño Singleton garante que unha clase particular teña só unha instancia/obxecto e un punto de acceso global.


Obxectivos de aprendizaxe

  • Como codificar usando un patrón de deseño singleton?

Comezando

As clases singleton úsanse para eliminar a instanciación de máis dun obxecto dunha clase particular.


 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; }

Avaría

  1. A iteración 1 _instance==null significa que só se crearán instancias.
  2. Iteración 2, como agora _intance !=null Así que se devolverán instancias creadas anteriormente.

Proba usando unha aplicación de consola

Chamemos dúas veces á clase singleton e asignemos a instancia devolta a dúas variables diferentes. Finalmente, verifique se ambos obxectos son iguais mediante a función Obxecto.Igual.


 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 devolve verdadeiro, significa que se produce unha única instancia cada vez.
  • Se devolve false, significa que a clase non está seguindo o patrón singleton.

Saída

A saída da consola devolve verdadeiro; parabéns. Implementou correctamente o patrón Singleton.



Seguridade do fío

A clase anterior coñécese como clase singleton, pero actualmente non é segura para fíos. Nun entorno multifío, dous fíos poden chegar á instrución if (_instance == null) ao mesmo tempo, e acabaremos tendo varias instancias dunha clase singleton.


Unha forma de crear un fío máis seguro é usar un mecanismo de bloqueo e a outra forma é facer unha instancia de só lectura para un enfoque máis limpo e eficiente.

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

Mostra Github

https://github.com/ssukhpinder/DesignPatterns

Grazas por ler!

Os patrocinios axúdanme a seguir mantendo e construíndo novos proxectos coma estes.


🙏 Se usas Pay, Noticed ou calquera dos meus proxectos, unha pequena contribución significaría MOITO. Por si só, o código aberto non paga as contas. Oxalá, coa túa axuda, continuar co meu traballo poida ser sostible, e non terei que ir buscar un traballo de verdade 😛.

Programación en C# 🚀

Grazas por formar parte da comunidade C#!

Compre café