Modèle de conception de stratégie

Dec 01 2022
résumer ce qui varie
Disons que nous avons une application qui permet aux clients de payer pour acheter un produit ou obtenir un service. L'application a plus d'un moyen de paiement, le client peut utiliser de l'argent, une carte visa ou un portefeuille, nous pouvons le faire comme ceci : En fait non, nous avons trois types de paiement et dans chaque cas il y a une implémentation différente donc nous avons un comportement "payer" de différentes manières, alors que pourrait-il arriver si nous prenions ce qui varie et le placions dans des classes séparées et utilisions une interface commune pour le comportement commun.

Disons que nous avons une application qui permet aux clients de payer pour acheter un produit ou obtenir un service.

L'application a plus d'un moyen de paiement, le client peut utiliser de l'argent, une carte visa ou un portefeuille, nous pouvons le faire comme ceci :

public class Customer {

  public void pay(String paymentType) {
    if (StringUtils.isEmpty(paymentType)) {
      throw new RuntimeException("please add payment method");
    }
      // pay according to payment type
    if (paymentType.equalsIgnoreCase("cash")) {
      // pay in cash steps ( Algorithm )
    } else if (paymentType.equalsIgnoreCase("visa")) {
      // use the visa for payment steps ( Algorithm )
    } else if (paymentType.equalsIgnoreCase("wallet")) {
      // use the wallet for payment steps ( Algorithm )
    } else {
      throw new RuntimeException(paymentType + "payment type is not available");
    }
  }
}

public class Client {
    public static void main(String[] args) {
        Customer customer = new Customer();
        customer.pay("cash");
    }
}

En fait non,

  1. Si vous souhaitez ajouter un nouveau mode de paiement, vous devez modifier cette classe, ce qui rend votre conception non flexible pour toute nouvelle modification. Par cette implémentation, nous violons l'un des principes de conception SOLID qui est : " les entités logicielles doivent être ouvertes pour extension mais fermé pour modification
  2. La plupart de la logique de la classe est inutilisée, une fois que le type de paiement est choisi par le client, donc si le client veut payer en espèces, le code qui gère le visa et le portefeuille ne sera pas utilisé.
  3. Comme vous le voyez dans cette manière de concevoir, vous devez gérer toutes les entrées de type de paiement possibles, même si cela n'a pas de sens, comme un type de paiement NULL ou un type de paiement vide, et vous pouvez facilement obliger le client à ne choisir que des types de paiement valides. , en utilisant un modèle de stratégie.

Nous avons trois types de paiement et dans chaque cas, il y a une implémentation différente, donc nous avons un comportement "payer" de différentes manières, alors que pourrait-il arriver si nous prenions ce qui varie et le placions dans des classes séparées et utilisions une interface commune pour le comportement commun .

Créons une interface appelée Payment . Il aura une méthode abstraite, et chaque méthode de paiement a sa classe concrète qui implémente l' interface de paiement . Le client aura l'interface de paiement en tant que variable "composition d'utilisation" et la méthode de type sera décidée par le choix du client.

public interface Payment {
    void pay();
}
public class CashPayment implements Payment {
    @Override
    public void pay() {
        // pay in cash steps ( Algorithm )
    }
}
public class VisaPayment implements Payment {
    @Override
    public void pay() {
        // use the visa for payment steps ( Algorithm )
    }
}
public class WalletPayment implements Payment {
    @Override
    public void pay() {
        // use the wallet for payment steps ( Algorithm )
    }
}

public class Customer {
    private Payment payment;

    public Customer(Payment payment) {
        this.payment = payment;
    }

    public void setPaymentType(Payment payment) {
        this.payment = payment;
    }

    public void pay() {
        payment.pay();
    }
}

public class Client {
    public static void main(String[] args) {
        Customer customer = new Customer(new VisaPayment());
        customer.pay();
        // you can change the payment type at runtime
        customer.setPaymentType(new CashPayment());
        customer.pay();
    }
}

Diagramme UML pour exemple de paiement client

Principes de conception

de la discussion précédente, nous pouvons conclure quatre principes de conception :

  1. Conception d'une interface non destinée à l'implémentation
  2. Encapsuler ce qui varie
  3. Les entités logicielles doivent être ouvertes pour extension mais fermées pour modification
  4. Utiliser la composition plutôt que l'héritage

La définition du livre pour le modèle de stratégie est : Définissez une famille d'algorithmes, encapsulez chacun d'eux et rendez-les interchangeables. La stratégie permet à l'algorithme de varier indépendamment des clients qui l'utilisent.

Dans notre exemple, la famille d'algorithmes était les méthodes de paiement, et nous encapsulons chacune d'entre elles dans la sous-classe concrète, et elles sont interchangeables par client qui peut choisir n'importe quel type de paiement même à l'exécution, l'algorithme est séparé du client et du client …. et c'est le modèle de stratégie .