Recherche…


Cacher les détails de la mise en œuvre de la stratégie

Une ligne directrice très courante dans la conception orientée objet est "aussi peu que possible mais autant que nécessaire". Cela s'applique également au modèle de stratégie: il est généralement conseillé de masquer les détails d'implémentation, par exemple les classes qui implémentent réellement des stratégies.

Pour les stratégies simples qui ne dépendent pas de paramètres externes, l'approche la plus courante consiste à rendre la classe d'implémentation elle-même privée (classes imbriquées) ou package-private et à exposer une instance via un champ statique d'une classe publique:

public class Animal {

  private static class AgeComparator implements Comparator<Animal> {
    public int compare(Animal a, Animal b) {
      return a.age() - b.age();
    }
  }

  // Note that this field has the generic type Comparator<Animal>, *not*
  // Animal.AgeComparator!
  public static final Comparator<Animal> AGE_COMPARATOR = new AgeComparator();

  private final int age;

  Animal(int age) {
    this.age = age;
  }

  public int age() {
    return age;
  }

}

List<Animal> myList = new LinkedList<>();
myList.add(new Animal(10));
myList.add(new Animal(5));
myList.add(new Animal(7));
myList.add(new Animal(9));

Collections.sort(
  myList,
  Animal.AGE_COMPARATOR
);

Le champ public Animal.AGE_COMPARATOR définit une stratégie qui peut ensuite être utilisée dans des méthodes telles que Collections.sort , mais cela ne nécessite pas que l'utilisateur sache rien de son implémentation, pas même de la classe d'implémentation.

Si vous préférez, vous pouvez utiliser un cours anonyme:

public class Animal {

  public static final Comparator<Animal> AGE_COMPARATOR = new Comparator<Animal> {
    public int compare(Animal a, Animal b) {
      return a.age() - b.age();
    }
  };

  // other members...
}

Si la stratégie est un peu plus complexe et nécessite des paramètres, il est très courant d'utiliser des méthodes d'usine statiques telles que Collections.reverseOrder(Comparator<T>) . Le type de retour de la méthode ne doit pas exposer de détails d'implémentation, par exemple reverseOrder() est implémenté comme

public static <T> Comparator<T> reverseOrder(Comparator<T> cmp) {
  // (Irrelevant lines left out.)
  return new ReverseComparator2<>(cmp);
}

Exemple de modèle de stratégie en Java avec classe de contexte

Stratégie:

Strategy est un modèle de comportement qui permet de modifier l’algorithme de façon dynamique à partir d’une famille d’algorithmes connexes.

UML du modèle de stratégie de Wikipedia

entrer la description de l'image ici :

import java.util.*;

/* Interface for Strategy */
interface OfferStrategy {
    public String getName();
    public double getDiscountPercentage();
}
/* Concrete implementation of base Strategy */
class NoDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0;
    }
}
/* Concrete implementation of base Strategy */
class QuarterDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0.25;
    }
}
/* Context is optional. But if it is present, it acts as single point of contact
   for client. 
   
   Multiple uses of Context
   1. It can populate data to execute an operation of strategy
   2. It can take independent decision on Strategy creation. 
   3. In absence of Context, client should be aware of concrete strategies. Context acts a wrapper and hides internals
   4. Code re-factoring will become easy
*/
class StrategyContext {
    double price; // price for some item or air ticket etc.
    Map<String,OfferStrategy> strategyContext = new HashMap<String,OfferStrategy>();
    StrategyContext(double price){
        this.price= price;
        strategyContext.put(NoDiscountStrategy.class.getName(),new NoDiscountStrategy());
        strategyContext.put(QuarterDiscountStrategy.class.getName(),new QuarterDiscountStrategy());        
    }
    public void applyStrategy(OfferStrategy strategy){
        /* 
        Currently applyStrategy has simple implementation. You can Context for populating some more information,
        which is required to call a particular operation            
        */
        System.out.println("Price before offer :"+price);
        double finalPrice = price - (price*strategy.getDiscountPercentage());
        System.out.println("Price after offer:"+finalPrice);
    }
    public OfferStrategy getStrategy(int monthNo){
        /*
            In absence of this Context method, client has to import relevant concrete Strategies everywhere.
            Context acts as single point of contact for the Client to get relevant Strategy
        */
        if ( monthNo < 6 )  {
            return strategyContext.get(NoDiscountStrategy.class.getName());
        }else{
            return strategyContext.get(QuarterDiscountStrategy.class.getName());
        }
        
    }
}
public class StrategyDemo{    
    public static void main(String args[]){
        StrategyContext context = new StrategyContext(100);
        System.out.println("Enter month number between 1 and 12");
        int month = Integer.parseInt(args[0]);
        System.out.println("Month ="+month);
        OfferStrategy strategy = context.getStrategy(month);
        context.applyStrategy(strategy);
    }
    
}

sortie:

Enter month number between 1 and 12
Month =1
Price before offer :100.0
Price after offer:100.0

Enter month number between 1 and 12
Month =7
Price before offer :100.0
Price after offer:75.0

Énoncé du problème: Offre 25% de réduction sur le prix de l'article pour les mois de juillet-décembre. Ne fournissez aucune réduction pour les mois de janvier à juin.

L'exemple ci-dessus montre l'utilisation du modèle de Strategy avec le Context . Context peut être utilisé comme point de contact unique pour le Client .

Deux stratégies - NoOfferStrategy et QuarterDiscountStrategy ont été déclarées conformément à l’énoncé du problème.

Comme indiqué dans la colonne de sortie, vous obtiendrez une remise en fonction du mois que vous avez entré

Cas d'utilisation du modèle de stratégie :

  1. Utilisez ce modèle lorsque vous disposez d'une famille d'algorithmes interchangeables et que vous devez modifier l'algorithme au moment de l'exécution.

  2. Gardez le code plus propre en supprimant les instructions conditionnelles

Modèle de stratégie sans classe de contexte / Java

Voici un exemple simple d'utilisation du modèle de stratégie sans classe de contexte. Deux stratégies d'implémentation implémentent l'interface et résolvent le même problème de différentes manières. Les utilisateurs de la classe EnglishTranslation peuvent appeler la méthode de traduction et choisir la stratégie à utiliser pour la traduction, en spécifiant la stratégie souhaitée.

// The strategy interface
public interface TranslationStrategy {
    String translate(String phrase);
}

// American strategy implementation 
public class AmericanTranslationStrategy implements TranslationStrategy {

    @Override
    public String translate(String phrase) {
        return phrase + ", bro";
    }
}

// Australian strategy implementation     
public class AustralianTranslationStrategy implements TranslationStrategy {

    @Override
    public String translate(String phrase) {
        return phrase + ", mate";
    }
}

// The main class which exposes a translate method
public class EnglishTranslation {

    //  translate a phrase using a given strategy
    public static String translate(String phrase, TranslationStrategy strategy) {
        return strategy.translate(phrase);
    }

    // example usage
    public static void main(String[] args) {

        // translate a phrase using the AustralianTranslationStrategy class
        String aussieHello = translate("Hello", new AustralianTranslationStrategy());
        // Hello, mate

        // translate a phrase using the AmericanTranslationStrategy class    
        String usaHello = translate("Hello", new AmericanTranslationStrategy());
        // Hello, bro
    }
}

Utilisation des interfaces fonctionnelles Java 8 pour implémenter le modèle de stratégie

Le but de cet exemple est de montrer comment nous pouvons réaliser le modèle de stratégie en utilisant les interfaces fonctionnelles Java 8. Nous allons commencer par un simple cas d’utilisation des codes en Java classique, puis le recoder sous la forme de Java 8.

L'exemple que nous utilisons est une famille d'algorithmes (stratégies) qui décrivent différentes manières de communiquer à distance.

La version Java classique

Le contrat pour notre famille d'algorithmes est défini par l'interface suivante:

public interface CommunicateInterface {
    public String communicate(String destination);
}

Ensuite, nous pouvons implémenter un certain nombre d’algorithmes, comme suit:

public class CommunicateViaPhone implements CommunicateInterface {
    @Override
    public String communicate(String destination) {
        return "communicating " + destination +" via Phone..";
    }
}

public class CommunicateViaEmail implements CommunicateInterface {
    @Override
    public String communicate(String destination) {
        return "communicating " + destination + " via Email..";
    }
}

public class CommunicateViaVideo implements CommunicateInterface {
    @Override
    public String communicate(String destination) {
        return "communicating " + destination + " via Video..";
    }
}

Ceux-ci peuvent être instanciés comme suit:

CommunicateViaPhone communicateViaPhone = new CommunicateViaPhone();
CommunicateViaEmail communicateViaEmail = new CommunicateViaEmail();
CommunicateViaVideo communicateViaVideo = new CommunicateViaVideo();

Ensuite, nous implémentons un service utilisant la stratégie:

public class CommunicationService {
    private CommunicateInterface communcationMeans;

    public void setCommuncationMeans(CommunicateInterface communcationMeans) {
        this.communcationMeans = communcationMeans;
    }

    public void communicate(String destination) {
        this.communcationMeans.communicate(destination);
    }
}

Finalement, nous pouvons utiliser les différentes stratégies comme suit:

CommunicationService communicationService = new CommunicationService();

// via phone
communicationService.setCommuncationMeans(communicateViaPhone);
communicationService.communicate("1234567");

// via email
communicationService.setCommuncationMeans(communicateViaEmail);
communicationService.communicate("[email protected]");

Utilisation des interfaces fonctionnelles Java 8

Le contrat des différentes implémentations d'algorithmes ne nécessite pas d'interface dédiée. Au lieu de cela, nous pouvons le décrire en utilisant l'interface existante java.util.function.Function<T, R> .

Les différents algorithmes composant the family of algorithms peuvent être exprimés sous forme d'expressions lambda. Cela remplace les classes de stratégie et leurs instanciations.

Function<String, String> communicateViaEmail = 
        destination -> "communicating " + destination + " via Email..";
Function<String, String> communicateViaPhone = 
        destination -> "communicating " + destination + " via Phone..";
Function<String, String> communicateViaVideo = 
        destination -> "communicating " + destination + " via Video..";

Ensuite, nous pouvons coder le "service" comme suit:

public class CommunicationService {
    private Function<String, String> communcationMeans;

    public void setCommuncationMeans(Function<String, String> communcationMeans) {
        this.communcationMeans = communcationMeans;
    }

    public void communicate(String destination) {
        this.communcationMeans.communicate(destination);
    }
}

Enfin, nous utilisons les stratégies comme suit

CommunicationService communicationService = new CommunicationService();

// via phone
communicationService.setCommuncationMeans(communicateViaPhone);
communicationService.communicate("1234567");

// via email
communicationService.setCommuncationMeans(communicateViaEmail);
communicationService.communicate("[email protected]");

Ou même:

communicationService.setCommuncationMeans(
    destination -> "communicating " + destination + " via Smoke signals.." );
CommunicationService.communicate("anyone");

Stratégie (PHP)

Exemple de www.phptherightway.com

<?php

interface OutputInterface
{
    public function load();
}

class SerializedArrayOutput implements OutputInterface
{
    public function load()
    {
        return serialize($arrayOfData);
    }
}

class JsonStringOutput implements OutputInterface
{
    public function load()
    {
        return json_encode($arrayOfData);
    }
}

class ArrayOutput implements OutputInterface
{
    public function load()
    {
        return $arrayOfData;
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow