Introducció al Patró Strategy

El patró de disseny Strategy és un patró de comportament que permet definir una família d'algoritmes, encapsular-los i fer-los intercanviables. Aquest patró permet que l'algoritme variï independentment dels clients que l'utilitzen. És especialment útil quan es vol evitar múltiples condicions if-else o switch-case per seleccionar diferents comportaments.

Objectius del Patró Strategy

  • Encapsulació d'algoritmes: Permet encapsular diferents algoritmes dins de classes separades.
  • Intercanviabilitat: Facilita la substitució d'un algoritme per un altre sense modificar el codi client.
  • Flexibilitat: Permet afegir nous algoritmes sense canviar el codi existent.

Estructura del Patró Strategy

El patró Strategy es compon de tres elements principals:

  1. Context: Manté una referència a l'objecte Strategy i delega el comportament a aquest objecte.
  2. Strategy: Defineix una interfície comuna per a tots els algoritmes.
  3. ConcreteStrategy: Implementa l'algoritme específic seguint la interfície Strategy.

Diagrama UML

+----------------+       +----------------+
|    Context     |       |    Strategy    |
|----------------|       |----------------|
| - strategy:    |<>---->| + algorithm()  |
|   Strategy     |       +----------------+
|----------------|              /_\
| + setStrategy()|               |
| + execute()    |               |
+----------------+               |
                                 |
                                 |
                         +---------------------+
                         |  ConcreteStrategyA  |
                         |---------------------|
                         | + algorithm()       |
                         +---------------------+
                         +---------------------+
                         |  ConcreteStrategyB  |
                         |---------------------|
                         | + algorithm()       |
                         +---------------------+

Exemple Pràctic

Definició de la Interfície Strategy

// Strategy.java
public interface Strategy {
    void execute();
}

Implementació de ConcreteStrategy

// ConcreteStrategyA.java
public class ConcreteStrategyA implements Strategy {
    @Override
    public void execute() {
        System.out.println("Algoritme A executat");
    }
}

// ConcreteStrategyB.java
public class ConcreteStrategyB implements Strategy {
    @Override
    public void execute() {
        System.out.println("Algoritme B executat");
    }
}

Implementació del Context

// Context.java
public class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy() {
        strategy.execute();
    }
}

Ús del Patró Strategy

public class StrategyPatternDemo {
    public static void main(String[] args) {
        Context context = new Context();

        // Utilitzar ConcreteStrategyA
        context.setStrategy(new ConcreteStrategyA());
        context.executeStrategy();  // Sortida: Algoritme A executat

        // Utilitzar ConcreteStrategyB
        context.setStrategy(new ConcreteStrategyB());
        context.executeStrategy();  // Sortida: Algoritme B executat
    }
}

Exercici Pràctic

Enunciat

Implementa un sistema de càlcul de descomptes utilitzant el patró Strategy. El sistema ha de permetre aplicar diferents tipus de descomptes (per exemple, descompte per percentatge, descompte fix, descompte per quantitat).

Solució

Definició de la Interfície Strategy

// DiscountStrategy.java
public interface DiscountStrategy {
    double applyDiscount(double price);
}

Implementació de ConcreteStrategy

// PercentageDiscountStrategy.java
public class PercentageDiscountStrategy implements DiscountStrategy {
    private double percentage;

    public PercentageDiscountStrategy(double percentage) {
        this.percentage = percentage;
    }

    @Override
    public double applyDiscount(double price) {
        return price - (price * (percentage / 100));
    }
}

// FixedDiscountStrategy.java
public class FixedDiscountStrategy implements DiscountStrategy {
    private double discount;

    public FixedDiscountStrategy(double discount) {
        this.discount = discount;
    }

    @Override
    public double applyDiscount(double price) {
        return price - discount;
    }
}

// QuantityDiscountStrategy.java
public class QuantityDiscountStrategy implements DiscountStrategy {
    private int quantity;
    private double discount;

    public QuantityDiscountStrategy(int quantity, double discount) {
        this.quantity = quantity;
        this.discount = discount;
    }

    @Override
    public double applyDiscount(double price) {
        return price - (quantity * discount);
    }
}

Implementació del Context

// ShoppingCart.java
public class ShoppingCart {
    private DiscountStrategy discountStrategy;

    public void setDiscountStrategy(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public double calculateTotal(double price) {
        return discountStrategy.applyDiscount(price);
    }
}

Ús del Patró Strategy

public class StrategyPatternDiscountDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        // Aplicar descompte per percentatge
        cart.setDiscountStrategy(new PercentageDiscountStrategy(10));
        System.out.println("Total amb descompte per percentatge: " + cart.calculateTotal(100));  // Sortida: 90.0

        // Aplicar descompte fix
        cart.setDiscountStrategy(new FixedDiscountStrategy(15));
        System.out.println("Total amb descompte fix: " + cart.calculateTotal(100));  // Sortida: 85.0

        // Aplicar descompte per quantitat
        cart.setDiscountStrategy(new QuantityDiscountStrategy(2, 5));
        System.out.println("Total amb descompte per quantitat: " + cart.calculateTotal(100));  // Sortida: 90.0
    }
}

Errors Comuns i Consells

  • No encapsular l'algoritme: Assegura't que cada algoritme estigui encapsulat en una classe separada que implementi la interfície Strategy.
  • No utilitzar la interfície Strategy: El context ha de dependre de la interfície Strategy, no de les implementacions concretes.
  • No permetre la intercanviabilitat: Assegura't que el context pugui canviar fàcilment d'algoritme en temps d'execució.

Resum

El patró Strategy és una eina poderosa per gestionar diferents algoritmes de manera flexible i intercanviable. Permet encapsular comportaments i seleccionar-los en temps d'execució, millorant la modularitat i mantenibilitat del codi. Amb aquest patró, pots afegir nous algoritmes sense modificar el codi existent, seguint el principi de tancament per modificació i obertura per extensió.

© Copyright 2024. Tots els drets reservats