Come lavorare con il modello di progettazione Decorator in C #

I modelli di progettazione sono soluzioni a problemi ricorrenti e complessità nella progettazione del software e sono classificati in tre categorie distinte: creazionale, strutturale e comportamentale.

Il motivo decorativo Decorator è un motivo strutturale e può essere utilizzato per aggiungere funzionalità a un oggetto in modo dinamico senza la necessità di modificare la struttura dell'oggetto. In sostanza, puoi sfruttare il motivo Decorator per associare funzionalità o comportamento a un oggetto in modo dinamico o statico senza la necessità di alterare la struttura dell'oggetto.

Si noti che il modello di progettazione Decorator segue il Principio aperto chiuso, uno dei principi SOLID. Per inciso, il principio aperto chiuso viene utilizzato per progettare classi aperte per estensioni ma chiuse per modifiche. La conformità al principio aperto chiuso facilita le applicazioni di costruzione che sono riutilizzabili e possono essere mantenute facilmente. La Gang of Four (GOF) di Dofactory afferma: "Attribuire dinamicamente responsabilità aggiuntive a un oggetto. I decoratori forniscono un'alternativa flessibile alla sottoclasse per estendere la funzionalità".

Un po 'di codice

In questa sezione esploreremo come implementare il modello di progettazione Decorator in C #. I partecipanti a un'implementazione tipica del modello di progettazione Decorator includono:

  1.  Componente: rappresenta il tipo di base del tipo effettivo o concreto
  2. Componente in calcestruzzo: rappresenta il tipo di calcestruzzo che estende il componente di base. Notare che le responsabilità o le funzionalità aggiuntive vengono aggiunte in questo tipo.
  3. Decoratore: rappresenta un riferimento a un componente. Le funzionalità dinamiche vengono aggiunte in questo tipo.

Consideriamo ora la seguente classe.

public abstract class Employee

   {

       public abstract string Display();

   }

Si noti che quando si utilizza il modello di progettazione Decorator, si estende il comportamento di una classe esistente ma non significa necessariamente che sia necessario utilizzare tipi astratti: i tipi possono o non possono essere astratti. È inoltre possibile implementare il modello di progettazione Decorator utilizzando interfacce o persino utilizzando metodi virtuali nelle classi concrete. In sostanza, non sei costretto a utilizzare solo classi astratte durante l'implementazione del modello di progettazione Decorator. Stiamo usando una classe astratta qui solo per motivi di semplicità.

La classe EmployeeConcrete estende la classe Employee e vi aggiunge ulteriori proprietà. Ecco come sarebbe questa classe.

   public class EmployeeConcrete : Employee

   {

       public string FirstName { set; get; }

       public string LastName { set; get; }

       public string Address { set; get; }

       public override string Display()

       {

           StringBuilder data = new StringBuilder();

           data.Append("First name: " + FirstName);

            data.Append("\nLast name: " + LastName);

           data.Append("\nAddress: " + Address);

           return data.ToString();

       }

   }

La classe EmployeeDecorator estende la classe Employee, accetta un'istanza della classe componente denominata Employee e sostituisce il metodo Display (). Ecco come sarebbe questa classe.

public class EmployeeDecorator : Employee

   {

       Employee employee = null;

       protected EmployeeDecorator(Employee employee)

       {

           this.employee = employee;

       }

       public override string Display()

       {

           return employee.Display();

       }

   }

Ora che il componente, il componente concreto e la classe decoratore sono pronti, ora puoi estendere la classe EmployeeDecorator per creare una classe decoratore concreto. Il seguente listato di codice mostra come apparirebbe questa classe.

public class PermanentEmployeeDecorator : EmployeeDecorator

   {

       //Add properties relevant to a permanent employee

       private double PF { get; set; }

       public PermanentEmployeeDecorator(Employee employee) : base(employee)

       {   }

       public override string Display()

       {

           return base.Display() + "\nEmployee type: Permanent";

       }

   }

E questo è tutto ciò che devi fare! È ora possibile creare un'istanza di PermanentEmployeeDecorator e utilizzarla come mostrato nello snippet di codice di seguito.

static void Main(string[] args)

       {

           EmployeeConcrete employeeConcrete = new EmployeeConcrete

         { FirstName = "Joydip", LastName = "Kanjilal", Address = "Hyderabad, India" };

           PermanentEmployeeDecorator employeeDecorator = new PermanentEmployeeDecorator(employeeConcrete);

           Console.WriteLine(employeeDecorator.Display());

           Console.Read();

       }

Puoi anche avere un altro tipo di dipendente: un dipendente a contratto. Per rappresentarlo, è necessario creare un'altra classe denominata ContractEmployeeDecorator che estende la classe EmployeeDecorator. Fare riferimento allo snippet di codice fornito di seguito.

public class ContractEmployeeDecorator : EmployeeDecorator

   {

       //Add properties relevant to a contract employee

       private double RatePerHour { get; set; }

       public ContractEmployeeDecorator(Employee employee) : base(employee)

       { }

       public override string Display()

       {

           return base.Display() + "\nEmployee type: Contractual";

       }

   }

Il frammento di codice seguente illustra come utilizzare la classe ContractEmployeeDecorator.

static void Main(string[] args)

       {

           EmployeeConcrete employeeConcrete = new EmployeeConcrete

{ FirstName = "Joydip", LastName = "Kanjilal", Address = "Hyderabad, India" };

           ContractEmployeeDecorator employeeDecorator = new ContractEmployeeDecorator(employeeConcrete);

           Console.WriteLine(employeeDecorator.Display());

           Console.Read();

       }