CSharp:Design Pattern Decorator
From Aino Wiki
Contents
Introduzione
E' un pattern progettuale di tipo Strutturale, consente di aggiungere dinamicamente nuove funzionalità (responsabilità) ad oggetti già esistenti. Questo viene realizzato costruendo una nuova classe decoratore che "avvolge" l'oggetto originale. Al costruttore del decoratore si passa come parametro l'oggetto originale. È altresì possibile passarvi un differente decoratore. In questo modo, più decoratori possono essere concatenati l'uno all'altro, aggiungendo così in modo incrementale funzionalità alla classe concreta (che è rappresentata dall'ultimo anello della catena).
La concatenazione dei decoratori può avvenire secondo una composizione arbitraria: il numero di comportamenti possibili dell'oggetto composto varia dunque sensibilmente rispetto al numero dei decoratori disponibili.
Questo pattern è una valida alternativa all'uso dell'ereditarietà singola o multipla. Con l'ereditarietà, infatti, l'aggiunta di funzionalità avviene staticamente secondo i legami definiti nella gerarchia di classi e non è possibile ottenere al run-time una combinazione arbitraria delle funzionalità, né la loro aggiunta/rimozione.
Diagramma preso da Wikipedia:
Esempio
Esempio frutto del corso su Lynda.com (di LinkedIn)Supponiamo di implementare un configuratore di auto, il prodotto finale non è prevedibile inizialmente ma dipende dalle scelte dell'utente. Un approccio mediante ereditarietà (con classi derivate) sarebbe complicato ma per avere il risultato finale dinamicamente si potrà elegantemente ottenere usando il Decorator.
Diagramma della classi generico che seguirà l'esempio proposto:
Codice
Seguendo il diagramma delle classi di prima, si son create delle cartelle ognuna relativa ad un rettangolo del diagramma. Segue La struttura della solution:
Component
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Decorator.Component { // Component public abstract class Car { public string Description { get; set; } public abstract string GetDescription(); public abstract double GetCarPrice(); } }
ConcreteComponent
CompactCar.cs
using Decorator.Component; namespace Decorator.ConcreteComponent { // ConcreteComponent public class CompactCar : Car { public CompactCar() { Description = "Compact Car"; } public override string GetDescription() => Description; public override double GetCarPrice() => 10000.00; } }
FullSizeCar.cs
using Decorator.Component; namespace Decorator.ConcreteComponent { // ConcreteComponent public class FullSizeCar : Car { public FullSizeCar() { Description = "FullSize Car"; } public override double GetCarPrice() => 30000.00; public override string GetDescription() => Description; } }
Decorator
Questa classe verrà ereditata al fine di usarla per aggiungere caratteristiche alla classe base
using Decorator.Component; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Decorator.Decorator { // Decorator public class CarDecorator : Car { protected Car _car; public CarDecorator(Car car) { _car = car; } public override double GetCarPrice() => _car.GetCarPrice(); public override string GetDescription() => _car.GetDescription(); } }
ConcreteDecorator
Sostanzialmente la funzione che aggiunge caratteristiche a quelle già eseistenti nella classe passata nel costruttore, è la concatenazione di stringhe ottenuta mediante una specie di ricorsone che risale tutte "Descrition" delle classi derivare. LeatherSeats.cs
using Decorator.Component; using Decorator.Decorator; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Decorator.ConcreteDecorator { public class LeatherSeats : CarDecorator { public LeatherSeats(Car car) : base(car) { Description = "Leather Seats"; } public override string GetDescription() => $"{_car.GetDescription()}, {Description}"; public override double GetCarPrice() => _car.GetCarPrice() + 2500; } }
Navigation.cs.cs
using Decorator.Component; using Decorator.Decorator; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Decorator.ConcreteDecorator { public class Navigation : CarDecorator { public Navigation(Car car) : base(car) { Description = "Navigation"; } public override string GetDescription() => $"{_car.GetDescription()}, {Description}"; public override double GetCarPrice() => _car.GetCarPrice() + 5000; } }
Sunroof.cs
using Decorator.Component; using Decorator.Decorator; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Decorator.ConcreteDecorator { public class Sunroof : CarDecorator { public Sunroof(Car car) : base(car) { Description = "Sunroof"; } public override string GetDescription() => $"{_car.GetDescription()}, {Description}"; public override double GetCarPrice() => _car.GetCarPrice() + 2500; } }
Program Main
using Decorator.Component; using Decorator.ConcreteComponent; using Decorator.ConcreteDecorator; using System; namespace Decorator { class Program { static void Main(string[] args) { Car theCar = new CompactCar(); theCar = new Navigation(theCar); theCar = new Sunroof(theCar); theCar = new LeatherSeats(theCar); Console.WriteLine(theCar.GetDescription()); Console.WriteLine($"{theCar.GetCarPrice():C2}"); Console.ReadKey(); } } }
Mappa e Link
C# | Design Pattern | Teoria
Parole chiave: