bauworld title

Patterns

2 - Strategy Pattern

Lo Strategy Pattern ha come obiettivo quello di spostare un algoritmo dalla classe in cui viene utilizzato in una separata: poiché ci possono essere problemi risolvibili con algoritmi (Strategies) differenti, si evita di averli tutti insieme in una singola classe (accumulo di codice e istruzioni condizionali), utilizzando invece la classe corretta per l'algoritmo che si vuole adottare.
L'esempio classico è quello dell'applicazione di un algoritmo di ordinamento (quicksort, mergesort etc.), utilizzando quello più corretto in base agli elementi che si vogliono ordinare (quantità di elementi, dispersione etc.).
Lo Strategy Pattern permette quindi di selezionare algoritmi diversi durante l'esecuzione.
Nell'esempio qui illustrato, abbiamo una classe che deve applicare una serie di sconti in percentuale ed in valore ad un prezzo lordo iniziale. Possiamo pensare ad algoritmi diversi:

Possiamo anche presumere che un utente possa avere necessità, in un momento successivo, di applicare una formula diversa.
Di seguito il diagramma UML delle classi che realizzano lo Strategy Pattern:

Analisi del codice

Nello Strategy Pattern gli algoritmi sono definiti con specifiche interfacce e le interfacce sono implementate dalle classi concrete strategy. Tutto cià permette di disaccoppiare gli algoritmi e le classi che li usano. L'algoritmo usato puà essere modificato senza intervenire sulla classe che lo usa; inoltre gli algoritmi possono essere modificati sia run-time che design-time. Nel nostro esempio vediamo l'interfaccia che richiede un'algoritmo per il calcolo del valore scontato:


    interface IDiscountCalculationStrategy
    {
        decimal Calculate(decimal fullPrice, List<Discount> list);
    }

Quella che segue è un esempio di classe concrete strategy che implementa l'interfaccia; è evidente che è possibile definire più concrete class per la stessa interfaccia:


    class DiscountCalcOne : IDiscountCalculationStrategy
    {
        public override decimal Calculate(decimal fullPrice, List<Discount> list)
        {
            //list.DiscountCalcOne(); not-implemented
            return 1;
        }
    }

L'interfaccia viene utilizzata dalla classe che la contiene (context strategy); questa classe, in funzione del tipo di strategy richiesto dall'oggetto che la istanzia, utilizzerà la concrete strategy corretta:


    class DiscountCalculation
    {
        private decimal _fullPrice;
        private readonly List<Discount> _discountList = new List<Discount>();
        private DiscountCalculationStrategy _discountCalculationStrategy;

        public DiscountCalculation(decimal fullPrice, List<Discount> discountList)
        {
            _discountList = discountList;
            _fullPrice = fullPrice;
        }

        public void SetDiscountCalculationStrategy(
		  DiscountCalculationStrategy discountCalculationStrategy)
        {
            _discountCalculationStrategy = discountCalculationStrategy;
        }

        public void AddDiscount(Discount _discount)
        {
            _discountList.Add(_discount);
        }

        public decimal Calculate()
        {
            return _discountCalculationStrategy.Calculate(_fullPrice, _discountList);
        }
    }

Scarica l'esempio

strategy.rar