108 votes

Exemple concret du modèle de stratégie

J'ai lu des articles sur le Principe de l'OCP et comment utiliser le modèle de stratégie pour y parvenir.

J'allais essayer d'expliquer cela à quelques personnes, mais le seul exemple auquel je peux penser est l'utilisation de différentes classes de validation en fonction du statut d'une "commande".

J'ai lu quelques articles en ligne, mais ils ne décrivent généralement pas une véritable raison d'utiliser la stratégie, comme la génération de rapports/factures/validation, etc...

Y a-t-il des exemples concrets où vous pensez qu'un modèle de stratégie est courant ?

11voto

tvanfosson Points 268301

J'ai une application qui synchronise chaque jour sa base d'utilisateurs avec notre annuaire d'entreprise. Les utilisateurs sont éligibles ou non éligibles en fonction de leur statut dans l'université. Chaque jour, le programme de provisionnement s'assure que ceux qui sont censés être éligibles sont provisionnés dans l'application et que ceux qui ne le sont pas sont déprovisionnés (en fait, selon un algorithme de dégradation progressive, mais ce n'est pas le sujet). Le samedi, je procède à une mise à jour plus approfondie qui synchronise certaines propriétés de chaque utilisateur et m'assure qu'il est éligible. À la fin du mois, j'effectue un traitement de refacturation basé sur l'utilisation du mois.

J'utilise un modèle de stratégie composable pour effectuer cette synchronisation. Le programme principal choisit essentiellement une stratégie maître en fonction du jour de la semaine (synchroniser les changements seulement/synchroniser tout) et du moment du semestre par rapport au calendrier universitaire. Si le cycle de facturation se termine, il le compose également avec une stratégie de facturation. Il exécute ensuite la stratégie choisie via une interface standard.

Je ne sais pas si c'est courant, mais j'ai eu l'impression que cela correspondait parfaitement au modèle de stratégie.

9voto

Un bon exemple de modèle de stratégie serait dans un jeu où nous pouvons avoir différents personnages et où chaque personnage peut avoir plusieurs armes pour attaquer mais ne peut utiliser qu'une seule arme à la fois. Nous avons donc le personnage comme contexte, par exemple Roi, Commandant, Chevalier, Soldat et l'arme comme stratégie où attack() pourrait être la méthode/algorithme qui dépend des armes utilisées. Ainsi, si les classes d'armes concrètes sont l'épée, la hache, l'arbalète, l'arc et la flèche, etc., elles implémenteront toutes la méthode attack(). Je suis sûr que de plus amples explications ne sont pas nécessaires.

8voto

Je sais que c'est une vieille question, mais je pense avoir un autre exemple intéressant que j'ai mis en œuvre récemment.

Il s'agit d'un exemple très concret de l'utilisation du modèle de stratégie dans un système de distribution de documents.

J'avais un système de livraison de PDF qui recevait une archive contenant beaucoup de documents et quelques métadonnées. Sur la base des métadonnées, il décidait où placer le document ; disons que, selon les données, je pouvais stocker le document dans A , B ou C systèmes de stockage, ou un mélange des trois.

Différents clients utilisaient ce système et avaient des exigences différentes en matière de retour en arrière et de traitement des erreurs : l'un d'entre eux souhaitait que le système de livraison s'arrête à la première erreur, qu'il laisse tous les documents déjà livrés dans leurs magasins, mais qu'il arrête le processus et ne livre plus rien ; un autre souhaitait qu'il revienne en arrière à partir de B en cas d'erreur lors du stockage dans C mais laisse ce qui a déjà été livré à A . Il est facile d'imaginer qu'un troisième ou un quatrième aura également des besoins différents.

Pour résoudre le problème, j'ai créé une classe de livraison de base qui contient la logique de livraison, ainsi que des méthodes de retour en arrière pour tous les stockages. Ces méthodes ne sont pas appelées directement par le système de livraison en cas d'erreur. Au lieu de cela, la classe utilise l'injection de dépendances pour recevoir une classe "Rollback / Error Handling Strategy" (basée sur le client utilisant le système), qui est appelée en cas d'erreurs, qui à son tour appelle les méthodes de retour en arrière si c'est approprié pour cette stratégie.

La classe de livraison elle-même rapporte ce qui se passe à la classe de stratégie (quels documents ont été livrés à quels magasins, et quels échecs se sont produits), et chaque fois qu'une erreur se produit, elle demande à la stratégie si elle doit continuer ou non. Si la stratégie dit "arrêtez", la classe appelle la méthode "cleanUp" de la stratégie, qui utilise les informations précédemment rapportées pour décider quelles méthodes de retour en arrière appeler depuis la classe de livraison, ou simplement ne rien faire.

rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);

if (rollbackStrategy.mustAbort()) {
    rollbackStrategy.rollback(); // rollback whatever is needed based on reports
    return false;
}

Donc, j'ai maintenant deux stratégies différentes : l'une est le QuitterStrategy (qui s'arrête à la première erreur et ne nettoie rien) et l'autre est la fonction MaximizeDeliveryToAStrategy (qui essaie autant que possible de ne pas interrompre le processus et de ne jamais revenir en arrière sur les données fournies au stockage A mais les retours en arrière se font à partir de B en cas de livraison à C échoue).

D'après ce que j'ai compris, il s'agit d'un exemple du modèle de stratégie. Si vous (oui, vous lisez) pensez que je me trompe, veuillez commenter ci-dessous et me le faire savoir. Je suis curieux de savoir ce qui constituerait une utilisation "pure" du modèle de stratégie, et quels aspects de mon implémentation violent la définition. Je pense que ça a l'air un peu bizarre parce que l'interface de la stratégie est un peu grosse. Tous les exemples que j'ai vus jusqu'à présent n'utilisent qu'une seule méthode, mais je pense toujours que cela encapsule un algorithme (si un morceau de logique commerciale peut être considéré comme un algorithme, ce que je pense).

Étant donné que la stratégie est également informée des événements survenant au cours de l'exécution de la livraison, elle peut également être considérée comme un élément de la stratégie. Observateur mais c'est une autre histoire.

En faisant quelques recherches, il semble qu'il s'agisse d'un "modèle composite" (comme le MVC, un modèle qui utilise plusieurs modèles de conception sous une forme particulière) appelé le Conseiller . C'est un conseiller pour savoir si la livraison doit continuer ou non, mais c'est aussi un gestionnaire d'erreurs actif puisqu'il peut revenir en arrière quand on le lui demande.

Quoi qu'il en soit, il s'agit d'un exemple assez complexe qui pourrait vous donner l'impression que les utilisations du modèle de stratégie sont trop simples ou stupides. Il peut être vraiment complexe et encore plus applicable lorsqu'il est utilisé avec d'autres modèles.

6voto

Jatinder Pal Points 651

Le modèle de stratégie est le plus couramment utilisé, notamment pour les validations et les algorithmes de tri.

Laissez-moi vous expliquer avec un simple exemple pratique

enum Speed {
  SLOW, MEDIUM, FAST;
}

class Sorter {
 public void sort(int[] input, Speed speed) {
    SortStrategy strategy = null;
    switch (speed) {
    case SLOW:
        strategy = new SlowBubbleSortStrategy();
        break;
    case MEDIUM:
        strategy = new MediumInsertationSortStrategy();
        break;

    case FAST:
        strategy = new FastQuickSortStrategy();
        break;
    default:
        strategy = new MediumInsertationSortStrategy();
    }
    strategy.sort(input);
 }

}

interface SortStrategy {

    public void sort(int[] input);
}

class SlowBubbleSortStrategy implements SortStrategy {

   public void sort(int[] input) {
    for (int i = 0; i < input.length; i++) {
        for (int j = i + 1; j < input.length; j++) {
            if (input[i] > input[j]) {
                int tmp = input[i];
                input[i] = input[j];
                input[j] = tmp;
            }
        }
    }
    System.out.println("Slow sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
  }

 }

class MediumInsertationSortStrategy implements SortStrategy {

public void sort(int[] input) {
    for (int i = 0; i < input.length - 1; i++) {
        int k = i + 1;
        int nxtVal = input[k];
        while (input[k - 1] > nxtVal) {
            input[k] = input[k - 1];
            k--;
            if (k == 0)
                break;
        }
        input[k] = nxtVal;
    }
    System.out.println("Medium sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }

 }

}

class FastQuickSortStrategy implements SortStrategy {

public void sort(int[] input) {
    sort(input, 0, input.length-1);
    System.out.println("Fast sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
}

private void sort(int[] input, int startIndx, int endIndx) {
    int endIndexOrig = endIndx;
    int startIndexOrig = startIndx;
    if( startIndx >= endIndx)
        return;
    int pavitVal = input[endIndx];
    while (startIndx <= endIndx) {
        while (input[startIndx] < pavitVal)
            startIndx++;
        while (input[endIndx] > pavitVal)
            endIndx--;
        if( startIndx <= endIndx){
            int tmp = input[startIndx];
            input[startIndx] = input[endIndx];
            input[endIndx] = tmp;
            startIndx++;
            endIndx--;
        }
    }
    sort(input, startIndexOrig, endIndx);
    sort(input, startIndx, endIndexOrig);
 }

}  

Le code de test pour cela est

public class StrategyPattern {
  public static void main(String[] args) {
    Sorter sorter = new Sorter();
    int[] input = new int[] {7,1,23,22,22,11,0,21,1,2,334,45,6,11,2};
    System.out.print("Input is : ");
    for (int i : input) {
        System.out.print(i + ",");
    }
    System.out.println();
    sorter.sort(input, Speed.SLOW);
 }

}

Le même exemple est tiré de http://coder2design.com/strategy-pattern/

4voto

Vivek Goel Points 190
public class StrategyDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        Item item1 = new Item("1234", 10);
        Item item2 = new Item("5678", 40);

        cart.addItem(item1);
        cart.addItem(item2);

        // pay by paypal
        cart.pay(new PaypalStrategy("myemail@example.com", "mypwd"));

        // pay by credit card
        cart.pay(new CreditCardStrategy("Pankaj Kumar", "1234567890123456", "786", "12/15"));
    }
}

interface PaymentStrategy {
    public void pay(int amount);
}

class CreditCardStrategy implements PaymentStrategy {

    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;

    public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate) {
        this.name = nm;
        this.cardNumber = ccNum;
        this.cvv = cvv;
        this.dateOfExpiry = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }

}

class PaypalStrategy implements PaymentStrategy {

    private String emailId;
    private String password;

    public PaypalStrategy(String email, String pwd) {
        this.emailId = email;
        this.password = pwd;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using Paypal.");
    }

}

class Item {

    private String upcCode;
    private int price;

    public Item(String upc, int cost) {
        this.upcCode = upc;
        this.price = cost;
    }

    public String getUpcCode() {
        return upcCode;
    }

    public int getPrice() {
        return price;
    }

}

class ShoppingCart {

    // List of items
    List<Item> items;

    public ShoppingCart() {
        this.items = new ArrayList<Item>();
    }

    public void addItem(Item item) {
        this.items.add(item);
    }

    public void removeItem(Item item) {
        this.items.remove(item);
    }

    public int calculateTotal() {
        int sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }

    public void pay(PaymentStrategy paymentMethod) {
        int amount = calculateTotal();
        paymentMethod.pay(amount);
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X